graphit 0.0.6 → 0.1.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.
- checksums.yaml +4 -4
- data/exe/graphit +60 -331
- data/lib/graphit.rb +5 -0
- data/lib/graphit/bitmap_drawing.rb +82 -0
- data/lib/graphit/bmp_file.rb +92 -0
- data/lib/graphit/color.rb +52 -0
- data/lib/graphit/graph.rb +243 -0
- data/lib/graphit/point.rb +11 -0
- data/lib/graphit/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1941e4e3ce7e786d8ea2396895269e98c0a9c5f0
|
4
|
+
data.tar.gz: 20bfe9f272774fc841b4d891cbb61c2f310ac63d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 320d9bd9482287b375e82cd0d45027b9476e574fefe202f0866c9bbd6130d626a62ab382e619516c74d3076943a20c4594c0e2bd15f195ef1d8873a6af712ace
|
7
|
+
data.tar.gz: 79a6028ffe3c3fd14d098b367c57ac4505a1b8521886e9d47ee73d895b86919f4711c2e256c9da537fc0cf9370a90075d3e6dfff6fb348d8af1f2ac6c0a8c27b
|
data/exe/graphit
CHANGED
@@ -6,21 +6,39 @@ require 'bin_utils' # For Speed
|
|
6
6
|
require 'optparse'
|
7
7
|
|
8
8
|
options = {}
|
9
|
+
options[:debug] = false
|
10
|
+
options[:verbose] = false
|
9
11
|
OptionParser.new do |opts|
|
10
12
|
opts.banner = "Usage: graphit [options] data"
|
11
13
|
|
14
|
+
opts.on("-d", "--[no-]debug", "Debug output") do |d|
|
15
|
+
options[:debug] = d
|
16
|
+
end
|
17
|
+
|
12
18
|
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
13
19
|
options[:verbose] = v
|
14
20
|
end
|
15
21
|
|
16
|
-
opts.on("
|
22
|
+
opts.on("-p", "--png", "Convert BMP to PNG. (Requires ImageMagick)") do |png|
|
23
|
+
options[:png] = png
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("--ymin [ymin]", String) do |ymin|
|
17
27
|
options[:ymin] = ymin
|
18
28
|
end
|
19
29
|
|
20
|
-
opts.on("--ymax [ymax]",
|
30
|
+
opts.on("--ymax [ymax]", String) do |ymax|
|
21
31
|
options[:ymax] = ymax
|
22
32
|
end
|
23
33
|
|
34
|
+
opts.on("--xmin [xmin]", String) do |xmin|
|
35
|
+
options[:xmin] = xmin
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("--xmax [xmax]", String) do |xmax|
|
39
|
+
options[:xmax] = xmax
|
40
|
+
end
|
41
|
+
|
24
42
|
opts.on("--height [h]", Integer) do |h|
|
25
43
|
options[:height] = h
|
26
44
|
end
|
@@ -43,352 +61,63 @@ OptionParser.new do |opts|
|
|
43
61
|
|
44
62
|
end.parse!
|
45
63
|
|
46
|
-
options[:outputfile]
|
47
|
-
|
48
|
-
@characters = Graphit.pixel_font
|
49
|
-
|
50
|
-
@h = options[:height] || 300
|
51
|
-
@w = options[:width] || 1050
|
64
|
+
options[:outputfile] ||= "graph.bmp"
|
52
65
|
|
53
|
-
|
54
|
-
|
66
|
+
options[:height] ||= 300
|
67
|
+
options[:width] ||= 1050
|
55
68
|
|
56
|
-
|
57
|
-
|
69
|
+
options[:ymin] ||= "auto"
|
70
|
+
options[:ymax] ||= "auto"
|
58
71
|
|
59
|
-
|
60
|
-
|
72
|
+
options[:xmin] ||= "auto"
|
73
|
+
options[:xmax] ||= "auto"
|
61
74
|
|
62
|
-
|
63
|
-
@
|
64
|
-
@
|
65
|
-
@right_padding = 10
|
66
|
-
|
67
|
-
@y_tics_mod = options[:yticsmod] || 10
|
68
|
-
@x_tics_mod = options[:xticsmod] || 3600
|
69
|
-
|
70
|
-
@graph_width = @w - @left_padding - @right_padding
|
71
|
-
@graph_height = @h - @bottom_padding - @top_padding
|
72
|
-
|
73
|
-
def recalculate_pixels_per_unit
|
74
|
-
@x_pixels_per_unit = @graph_width.to_f / (@xmax.to_f - @xmin.to_f)
|
75
|
-
@y_pixels_per_unit = @graph_height.to_f / (@ymax.to_f - @ymin.to_f)
|
76
|
-
|
77
|
-
if @xmax - @xmin < (86400 * 3.5)
|
78
|
-
@x_tics_mod = 3600
|
79
|
-
elsif @xmax - @xmin < (86400 * 7.5)
|
80
|
-
@x_tics_mod = (86400 * 24)
|
81
|
-
elsif @xmax - @xmin < (86400 * 50)
|
82
|
-
@x_tics_mod = (86400 * 24 * 7)
|
83
|
-
elsif @xmax - @xmin < (86400 * 800)
|
84
|
-
@x_tics_mod = (86400 * 24 * 30)
|
85
|
-
else
|
86
|
-
@x_tics_mod = 3600
|
87
|
-
end
|
88
|
-
|
89
|
-
puts "@x_pixels_per_unit: #{@x_pixels_per_unit}"
|
90
|
-
puts "@y_pixels_per_unit: #{@y_pixels_per_unit}"
|
75
|
+
if options[:debug]
|
76
|
+
puts "h: #{@h}, w: #{@w}"
|
77
|
+
puts "ymin: #{@ymin}, ymax: #{@ymax}"
|
91
78
|
end
|
92
79
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
point[:x] = @xmin
|
98
|
-
elsif point[:x] > @xmax
|
99
|
-
point[:x] = @xmax
|
100
|
-
end
|
101
|
-
|
102
|
-
if point[:y] < @ymin
|
103
|
-
point[:y] = @ymin
|
104
|
-
elsif point[:y] > @ymax
|
105
|
-
point[:y] = @ymax
|
106
|
-
end
|
107
|
-
|
108
|
-
#puts "Mid x: #{point[:x]}, y: #{point[:y]}"
|
109
|
-
|
110
|
-
x = @left_padding + (point[:x] - @xmin) * @x_pixels_per_unit
|
111
|
-
y = @h - @bottom_padding - ((point[:y] - @ymin) * @y_pixels_per_unit)
|
112
|
-
|
113
|
-
#puts "Out x: #{x}, y: #{y}"
|
114
|
-
|
115
|
-
return { x: x, y: y }
|
116
|
-
end
|
80
|
+
options[:bottom_padding] ||= 25
|
81
|
+
options[:top_padding] ||= 10
|
82
|
+
options[:left_padding] ||= 100
|
83
|
+
options[:right_padding] ||= 10
|
117
84
|
|
118
|
-
|
119
|
-
|
120
|
-
if @characters[c].nil?
|
121
|
-
# skip
|
122
|
-
else
|
123
|
-
@characters[c].each_with_index do |row, y|
|
124
|
-
row.each_with_index do |col, x|
|
125
|
-
if col == 0
|
126
|
-
# Do nothing
|
127
|
-
else
|
128
|
-
thisY = px + x
|
129
|
-
thisX = py + y
|
130
|
-
|
131
|
-
pixels[thisX][thisY] = color unless pixels[thisX][thisY].nil?
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
px += @characters[c].size + 1
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
return pixels
|
141
|
-
end
|
85
|
+
options[:yticsmod] ||= 10
|
86
|
+
options[:xticsmod] ||= 3600
|
142
87
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
d = 0.0
|
147
|
-
|
148
|
-
while d < 1
|
149
|
-
if x2 == x1
|
150
|
-
x = x1
|
151
|
-
else
|
152
|
-
x = x1 + (x2.to_f-x1.to_f) * d
|
153
|
-
end
|
154
|
-
|
155
|
-
y = y1 + (y2.to_f-y1.to_f) * d
|
156
|
-
|
157
|
-
pixels[y.to_i][x.to_i] = color unless pixels[y.to_i][x.to_i].nil?
|
158
|
-
|
159
|
-
d += 1.0 / ( len.to_f * 2.0 )
|
160
|
-
end
|
161
|
-
|
162
|
-
return pixels
|
163
|
-
end
|
88
|
+
options[:data_color] ||= Graphit::Color.blue_color
|
89
|
+
options[:background_color] ||= Graphit::Color.light_light_gray_color
|
164
90
|
|
165
|
-
|
166
|
-
|
167
|
-
# Horizontal Grid lines
|
168
|
-
(@ymin.to_i..@ymax.to_i).each do |y|
|
169
|
-
if y % @y_tics_mod == 0
|
170
|
-
p1 = translate_data_point_to_graph_point( { y: y, x: @xmin } )
|
171
|
-
p2 = translate_data_point_to_graph_point( { y: y, x: @xmax } )
|
172
|
-
|
173
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, [0xAA,0xAA,0xAA] )
|
174
|
-
|
175
|
-
# Dunno why 60. Can't figure it out. I'm sure it's because I'm dumb.
|
176
|
-
pixels = draw_text( "#{y}", p1[:x] - (10 * y.to_s.size), p1[:y], pixels, [0x00,0x00,0x00] )
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
#Vertical Grid Lines
|
181
|
-
(@xmin..@xmax).each do |x|
|
182
|
-
if x % @x_tics_mod == 0
|
183
|
-
p1 = translate_data_point_to_graph_point( { x: x, y: @ymin } )
|
184
|
-
p2 = translate_data_point_to_graph_point( { x: x, y: @ymax } )
|
185
|
-
|
186
|
-
#Manual offset for GMT-7
|
187
|
-
if (x + (3600*-7)) % 86400 == 0
|
188
|
-
lcolor = [0x00,0x00,0xFF]
|
189
|
-
else
|
190
|
-
lcolor = [0xAA,0xAA,0xAA]
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, lcolor )
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# Graph Outline
|
199
|
-
p1 = translate_data_point_to_graph_point( { x: @xmin, y: @ymin } )
|
200
|
-
p2 = translate_data_point_to_graph_point( { x: @xmax, y: @ymin } )
|
201
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, [0x22,0x22,0x22] )
|
202
|
-
p1 = translate_data_point_to_graph_point( { x: @xmax, y: @ymin } )
|
203
|
-
p2 = translate_data_point_to_graph_point( { x: @xmax, y: @ymax } )
|
204
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, [0x22,0x22,0x22] )
|
205
|
-
p1 = translate_data_point_to_graph_point( { x: @xmax, y: @ymax } )
|
206
|
-
p2 = translate_data_point_to_graph_point( { x: @xmin, y: @ymax } )
|
207
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, [0x22,0x22,0x22] )
|
208
|
-
p1 = translate_data_point_to_graph_point( { x: @xmin, y: @ymax } )
|
209
|
-
p2 = translate_data_point_to_graph_point( { x: @xmin, y: @ymin } )
|
210
|
-
pixels = draw_line( p1[:x], p1[:y], p2[:x], p2[:y], pixels, [0x22,0x22,0x22] )
|
211
|
-
|
212
|
-
|
213
|
-
data.each_with_index do |p, i|
|
214
|
-
data[i] = translate_data_point_to_graph_point( p )
|
215
|
-
end
|
216
|
-
|
217
|
-
data.each_with_index do |point, i|
|
218
|
-
if i < data.size - 1
|
219
|
-
x1 = point[:x]
|
220
|
-
y1 = point[:y]
|
221
|
-
|
222
|
-
x2 = data[i+1][:x]
|
223
|
-
y2 = data[i+1][:y]
|
224
|
-
|
225
|
-
pixels = draw_line( x1, y1, x2, y2, pixels, color )
|
226
|
-
end
|
227
|
-
end
|
91
|
+
data_to_graph = []
|
228
92
|
|
229
|
-
|
230
|
-
|
93
|
+
xmin = 9999999999
|
94
|
+
xmax = 0
|
231
95
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
w = @w
|
238
|
-
|
239
|
-
pixels = []
|
240
|
-
(1..h).each do |y|
|
241
|
-
row = []
|
242
|
-
(1..w).each do |x|
|
243
|
-
row.push( [0xEE,0xEE,0xEE] )
|
244
|
-
end
|
245
|
-
pixels.push(row)
|
246
|
-
end
|
247
|
-
|
248
|
-
puts "Generating pixels: #{Time.now.to_f - start}"
|
249
|
-
|
250
|
-
# (0..499).each do |n|
|
251
|
-
#
|
252
|
-
# i = (n/100.0) * (2 * Math::PI)
|
253
|
-
#
|
254
|
-
# y = 50 + (40 * Math.sin(i))
|
255
|
-
#
|
256
|
-
# pixels[y][n] = [0x00,0x00,0xFF]
|
257
|
-
# end
|
258
|
-
#
|
259
|
-
# (0..499).each do |n|
|
260
|
-
#
|
261
|
-
# i = (n/100.0) * (2 * Math::PI)
|
262
|
-
#
|
263
|
-
# y = 50 + (40 * Math.cos(i))
|
264
|
-
#
|
265
|
-
# pixels[y][n] = [0xFF,0x00,0x00]
|
266
|
-
# end
|
96
|
+
# Expected Input:
|
97
|
+
# x y
|
98
|
+
ARGF.each_line do |line|
|
99
|
+
# puts line
|
100
|
+
l = line.strip.split(" ")
|
267
101
|
|
268
|
-
|
269
|
-
# pixels = draw_line( 40, 65, 900, 250, pixels, [0x00,0xFF,0xFF] )
|
270
|
-
#
|
271
|
-
# pixels = draw_line( 50, 50, w, 50, pixels, [0xAA,0xAA,0xAA] )
|
272
|
-
# pixels = draw_line( 50, 100, w, 100, pixels, [0xAA,0xAA,0xAA] )
|
273
|
-
# pixels = draw_line( 50, 150, w, 150, pixels, [0xAA,0xAA,0xAA] )
|
102
|
+
next unless l.size == 2
|
274
103
|
|
275
|
-
|
104
|
+
x = l[0].to_i
|
105
|
+
y = l[1].to_f
|
276
106
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
ARGF.each_line do |line|
|
281
|
-
# puts line
|
282
|
-
l = line.strip.split(":")
|
283
|
-
|
284
|
-
next unless l.size == 2
|
285
|
-
|
286
|
-
x = l[0].to_i
|
287
|
-
y = l[1].to_f
|
288
|
-
|
289
|
-
@xmin = x if x < @xmin
|
290
|
-
@xmax = x if x > @xmax
|
291
|
-
|
292
|
-
data_to_graph.push( { x: x, y: y } )
|
293
|
-
end
|
107
|
+
xmin = x if x < xmin
|
108
|
+
xmax = x if x > xmax
|
294
109
|
|
295
|
-
|
296
|
-
|
110
|
+
data_to_graph.push( Graphit::Point.new( x, y ) )
|
111
|
+
end
|
297
112
|
|
298
|
-
|
299
|
-
|
300
|
-
recalculate_pixels_per_unit
|
301
|
-
|
302
|
-
# (0..140).each do |n|
|
303
|
-
# point = { x: n * 10, y: @ymax - rand( @ymax ).to_i }
|
304
|
-
# data_to_graph.push( translate_data_point_to_graph_point( point ) )
|
305
|
-
# end
|
306
|
-
|
307
|
-
graph_data( data_to_graph, pixels, [0xEE,0x00,0x00] )
|
308
|
-
|
309
|
-
@xmin = @xmax if @xmin > @xmax
|
310
|
-
|
311
|
-
(@xmin..@xmax).each do |x|
|
312
|
-
|
313
|
-
if x % 3600 == 0
|
314
|
-
x_offset = translate_data_point_to_graph_point( { x: x, y: 10 } )[:x].to_i
|
315
|
-
|
316
|
-
h = Time.at(x).hour
|
317
|
-
|
318
|
-
if h % 2 == 0
|
319
|
-
puts "h: #{h.to_s}, x: #{x_offset}"
|
320
|
-
pixels = draw_text( h.to_s, x_offset, @h - 15, pixels, [0x00,0x00,0x00] )
|
321
|
-
end
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
|
326
|
-
pixel_data = ""
|
113
|
+
@graph = Graphit::Graph.new( options, data_to_graph )
|
327
114
|
|
328
|
-
|
329
|
-
# ["FF0000", "00FF00", "FF0000", "00FF00"] ]
|
330
|
-
|
331
|
-
height = pixels.size
|
332
|
-
width = pixels[0].size
|
333
|
-
|
334
|
-
start = Time.now.to_f
|
335
|
-
|
336
|
-
pixels.reverse.each_with_index do |row, i|
|
337
|
-
row_bytes = 0
|
338
|
-
row.each do |pixel|
|
339
|
-
|
340
|
-
BinUtils.append_int8!(pixel_data, pixel[0])
|
341
|
-
BinUtils.append_int8!(pixel_data, pixel[1])
|
342
|
-
BinUtils.append_int8!(pixel_data, pixel[2])
|
343
|
-
|
344
|
-
#pixel_data += [pixel[0,2].hex].pack( "C" )
|
345
|
-
#pixel_data += [pixel[2,2].hex].pack( "C" )
|
346
|
-
#pixel_data += [pixel[4,2].hex].pack( "C" )
|
347
|
-
|
348
|
-
row_bytes += 3
|
349
|
-
end
|
350
|
-
|
351
|
-
# Padding
|
352
|
-
bytes_to_pad = row_bytes % 4
|
353
|
-
|
354
|
-
# puts "Row: #{i}, bytes: #{row_bytes}, padding: #{bytes_to_pad}"
|
355
|
-
|
356
|
-
bytes_to_pad.times do
|
357
|
-
BinUtils.append_int8!(pixel_data, 0x00)
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
puts "Generating pixel data string: #{Time.now.to_f - start}"
|
362
|
-
|
363
|
-
|
364
|
-
file_size = 54 + pixel_data.size
|
115
|
+
puts @graph
|
365
116
|
|
366
|
-
|
367
|
-
f.print [0x4D].pack( "C" )
|
368
|
-
f.print [file_size].pack( "L" )
|
369
|
-
|
370
|
-
f.print [0x00].pack( "C" ) # Unused
|
371
|
-
f.print [0x00].pack( "C" )
|
372
|
-
f.print [0x00].pack( "C" )
|
373
|
-
f.print [0x00].pack( "C" )
|
374
|
-
|
375
|
-
f.print [54].pack( "L" ) # Pixel Array Offset
|
376
|
-
|
377
|
-
f.print [0x28, 0x00, 0x00, 0x00].pack( "L" ) #Number of bytes in the DIB header (from this point)
|
117
|
+
@bmp = Graphit::BmpFile.new( @graph.bitmap_drawing )
|
378
118
|
|
379
|
-
|
380
|
-
f.print [height].pack( "L" ) #Height
|
381
|
-
|
382
|
-
f.print [0x01, 0x00].pack( "S" ) # Number of color planes being used
|
383
|
-
f.print [0x18, 0x00].pack( "S" ) # Number of bits per pixel
|
384
|
-
f.print [0x00].pack( "L" ) # BI_RGB, no pixel array compression used
|
385
|
-
f.print [pixel_data.size].pack( "L" ) # Size of the raw bitmap data (including padding)
|
386
|
-
f.print [0x130B].pack( "L" ) #2835 pixels/meter horizontal
|
387
|
-
f.print [0x130B].pack( "L" ) #2835 pixels/meter vertical
|
388
|
-
f.print [0x00].pack( "L" ) # Number of colors in the palette
|
389
|
-
f.print [0x00].pack( "L" ) #0 means all colors are important
|
390
|
-
|
391
|
-
f.print pixel_data
|
392
|
-
end
|
119
|
+
@bmp.save_to_file( options[:outputfile] )
|
393
120
|
|
394
|
-
|
121
|
+
if options[:png]
|
122
|
+
`convert #{options[:outputfile]} #{options[:outputfile].gsub('bmp', 'png')}`
|
123
|
+
end
|
data/lib/graphit.rb
CHANGED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Graphit
|
2
|
+
class BitmapDrawing
|
3
|
+
|
4
|
+
attr_accessor :pixels
|
5
|
+
|
6
|
+
def initialize( height, width, background_color = [0x00,0x00,0x00] )
|
7
|
+
background_color = background_color.to_hex_array if background_color.class == Color
|
8
|
+
|
9
|
+
self.pixels = []
|
10
|
+
(1..height).each do |y|
|
11
|
+
row = []
|
12
|
+
(1..width).each do |x|
|
13
|
+
row.push( background_color )
|
14
|
+
end
|
15
|
+
self.pixels.push(row)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def height
|
20
|
+
self.pixels.size rescue 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def width
|
24
|
+
self.pixels[0].size rescue 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def draw_text( text, point, color = [0x00,0x00,0x00] )
|
28
|
+
@characters = Graphit.pixel_font
|
29
|
+
color = color.to_hex_array if color.class == Color
|
30
|
+
|
31
|
+
px = point.x
|
32
|
+
|
33
|
+
text.each_char do |c|
|
34
|
+
if @characters[c].nil?
|
35
|
+
# skip
|
36
|
+
else
|
37
|
+
@characters[c].each_with_index do |row, y|
|
38
|
+
row.each_with_index do |col, x|
|
39
|
+
if col == 0
|
40
|
+
# Do nothing
|
41
|
+
else
|
42
|
+
thisY = px + x
|
43
|
+
thisX = point.y + y
|
44
|
+
|
45
|
+
self.pixels[thisX][thisY] = color unless pixels[thisX][thisY].nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
px += @characters[c].size + 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
return self.pixels
|
55
|
+
end
|
56
|
+
|
57
|
+
def draw_line( start_point, end_point, color )
|
58
|
+
color = color.to_hex_array if color.class == Color
|
59
|
+
|
60
|
+
len = Math.sqrt( (end_point.x-start_point.x)**2 + (end_point.y - start_point.y)**2 )
|
61
|
+
|
62
|
+
d = 0.0
|
63
|
+
|
64
|
+
while d < 1
|
65
|
+
if end_point.x == start_point.x
|
66
|
+
x = start_point.x
|
67
|
+
else
|
68
|
+
x = start_point.x + (end_point.x-start_point.x) * d
|
69
|
+
end
|
70
|
+
|
71
|
+
y = start_point.y + (end_point.y-start_point.y) * d
|
72
|
+
|
73
|
+
self.pixels[y.to_i][x.to_i] = color unless self.pixels[y.to_i][x.to_i].nil?
|
74
|
+
|
75
|
+
d += 1.0 / ( len * 2.0 )
|
76
|
+
end
|
77
|
+
|
78
|
+
return self.pixels
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Graphit
|
2
|
+
class BmpFile
|
3
|
+
|
4
|
+
attr_accessor :bitmap_drawing
|
5
|
+
|
6
|
+
def initialize( bitmap_drawing )
|
7
|
+
self.bitmap_drawing = bitmap_drawing
|
8
|
+
end
|
9
|
+
|
10
|
+
def save_to_file( filename )
|
11
|
+
File.open( filename, 'w') do |f|
|
12
|
+
|
13
|
+
start = Time.now.to_f
|
14
|
+
|
15
|
+
# if options[:debug]
|
16
|
+
# puts "Generating pixels: #{Time.now.to_f - start}"
|
17
|
+
# end
|
18
|
+
|
19
|
+
# if options[:debug]
|
20
|
+
# puts data_to_graph.size
|
21
|
+
# puts "Data points ^"
|
22
|
+
# puts "Max: #{@xmax}, Min: #{@xmin}"
|
23
|
+
# end
|
24
|
+
|
25
|
+
|
26
|
+
height = self.bitmap_drawing.height
|
27
|
+
width = self.bitmap_drawing.width
|
28
|
+
|
29
|
+
start = Time.now.to_f
|
30
|
+
|
31
|
+
pixel_data = ""
|
32
|
+
self.bitmap_drawing.pixels.reverse.each_with_index do |row, i|
|
33
|
+
row_bytes = 0
|
34
|
+
row.each do |pixel|
|
35
|
+
BinUtils.append_int8!(pixel_data, pixel[0])
|
36
|
+
BinUtils.append_int8!(pixel_data, pixel[1])
|
37
|
+
BinUtils.append_int8!(pixel_data, pixel[2])
|
38
|
+
|
39
|
+
#pixel_data += [pixel[0,2].hex].pack( "C" )
|
40
|
+
#pixel_data += [pixel[2,2].hex].pack( "C" )
|
41
|
+
#pixel_data += [pixel[4,2].hex].pack( "C" )
|
42
|
+
|
43
|
+
row_bytes += 3
|
44
|
+
end
|
45
|
+
|
46
|
+
# Padding
|
47
|
+
bytes_to_pad = row_bytes % 4
|
48
|
+
|
49
|
+
# puts "Row: #{i}, bytes: #{row_bytes}, padding: #{bytes_to_pad}"
|
50
|
+
|
51
|
+
bytes_to_pad.times do
|
52
|
+
BinUtils.append_int8!(pixel_data, 0x00)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# if options[:debug]
|
57
|
+
# puts "Generating pixel data string: #{Time.now.to_f - start}"
|
58
|
+
# end
|
59
|
+
|
60
|
+
file_size = 54 + pixel_data.size
|
61
|
+
|
62
|
+
f.print [0x42].pack ("C")
|
63
|
+
f.print [0x4D].pack( "C" )
|
64
|
+
f.print [file_size].pack( "L" )
|
65
|
+
|
66
|
+
f.print [0x00].pack( "C" ) # Unused
|
67
|
+
f.print [0x00].pack( "C" )
|
68
|
+
f.print [0x00].pack( "C" )
|
69
|
+
f.print [0x00].pack( "C" )
|
70
|
+
|
71
|
+
f.print [54].pack( "L" ) # Pixel Array Offset
|
72
|
+
|
73
|
+
f.print [0x28, 0x00, 0x00, 0x00].pack( "L" ) #Number of bytes in the DIB header (from this point)
|
74
|
+
|
75
|
+
f.print [width].pack( "L" ) #Width of the bitmap in pixels
|
76
|
+
f.print [height].pack( "L" ) #Height
|
77
|
+
|
78
|
+
f.print [0x01, 0x00].pack( "S" ) # Number of color planes being used
|
79
|
+
f.print [0x18, 0x00].pack( "S" ) # Number of bits per pixel
|
80
|
+
f.print [0x00].pack( "L" ) # BI_RGB, no pixel array compression used
|
81
|
+
f.print [pixel_data.size].pack( "L" ) # Size of the raw bitmap data (including padding)
|
82
|
+
f.print [0x130B].pack( "L" ) #2835 pixels/meter horizontal
|
83
|
+
f.print [0x130B].pack( "L" ) #2835 pixels/meter vertical
|
84
|
+
f.print [0x00].pack( "L" ) # Number of colors in the palette
|
85
|
+
f.print [0x00].pack( "L" ) #0 means all colors are important
|
86
|
+
|
87
|
+
f.print pixel_data
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Graphit
|
2
|
+
class Color
|
3
|
+
|
4
|
+
# 0 - 255
|
5
|
+
attr_accessor :red
|
6
|
+
attr_accessor :green
|
7
|
+
attr_accessor :blue
|
8
|
+
|
9
|
+
def initialize( red, green, blue )
|
10
|
+
self.red = red
|
11
|
+
self.green = green
|
12
|
+
self.blue = blue
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hex_array
|
16
|
+
[blue,green,red]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.light_light_gray_color
|
20
|
+
Graphit::Color.new( 225, 225, 225 )
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.light_gray_color
|
24
|
+
Graphit::Color.new( 204, 204, 204 )
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.medium_gray_color
|
28
|
+
Graphit::Color.new( 170, 170, 170 )
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.white_color
|
32
|
+
Graphit::Color.new( 255, 255, 255 )
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.black_color
|
36
|
+
Graphit::Color.new( 0, 0, 0 )
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.red_color
|
40
|
+
Graphit::Color.new( 255, 0, 0 )
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.blue_color
|
44
|
+
Graphit::Color.new( 0, 0, 255 )
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.green_color
|
48
|
+
Graphit::Color.new( 0, 255, 0 )
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
module Graphit
|
2
|
+
class Graph
|
3
|
+
|
4
|
+
attr_accessor :bitmap_drawing
|
5
|
+
|
6
|
+
attr_accessor :graph_data
|
7
|
+
|
8
|
+
attr_accessor :height, :width
|
9
|
+
attr_accessor :xmin, :xmax, :xticsmod
|
10
|
+
attr_accessor :ymin, :ymax, :yticsmod
|
11
|
+
attr_accessor :background_color
|
12
|
+
attr_accessor :verbose
|
13
|
+
attr_accessor :debug
|
14
|
+
attr_accessor :left_padding, :right_padding, :top_padding, :bottom_padding
|
15
|
+
attr_accessor :x_pixels_per_unit, :y_pixels_per_unit
|
16
|
+
attr_accessor :data_color
|
17
|
+
|
18
|
+
def initialize( options, data )
|
19
|
+
|
20
|
+
options.each do |k,v|
|
21
|
+
if self.respond_to?("#{k}=".to_sym)
|
22
|
+
self.send( "#{k}=".to_sym, v )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
self.graph_data = data
|
27
|
+
|
28
|
+
recalculate_xmin if xmin.to_s == "auto"
|
29
|
+
recalculate_xmax if xmax.to_s == "auto"
|
30
|
+
recalculate_ymin if ymin.to_s == "auto"
|
31
|
+
recalculate_ymax if ymax.to_s == "auto"
|
32
|
+
|
33
|
+
# Ensure floats
|
34
|
+
self.xmin = self.xmin.to_f
|
35
|
+
self.xmax = self.xmax.to_f
|
36
|
+
self.ymin = self.ymin.to_f
|
37
|
+
self.ymax = self.ymax.to_f
|
38
|
+
|
39
|
+
recalculate_pixels_per_unit
|
40
|
+
|
41
|
+
puts "BGColor: #{options[:background_color]}"
|
42
|
+
|
43
|
+
self.bitmap_drawing = BitmapDrawing.new( options[:height], options[:width], options[:background_color] )
|
44
|
+
|
45
|
+
draw_horizontal_gridlines
|
46
|
+
draw_vertical_gridlines
|
47
|
+
draw_graph_outline
|
48
|
+
draw_xaxis_labels
|
49
|
+
|
50
|
+
draw_graph( data )
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
s = super
|
55
|
+
s += "\n"
|
56
|
+
s += " Height: #{height}, Width: #{width}\n"
|
57
|
+
s += " Xmin: #{xmin}, Xmax: #{xmax}, Xticsmod: #{xticsmod}\n"
|
58
|
+
s += " Ymin: #{ymin}, Ymax: #{ymax}, Yticsmod: #{yticsmod}\n"
|
59
|
+
s
|
60
|
+
end
|
61
|
+
|
62
|
+
def recalculate_xmin
|
63
|
+
puts "recalculate_xmin" if self.debug
|
64
|
+
|
65
|
+
self.xmin = self.graph_data.min_by{ |p| p.x.to_f }.x
|
66
|
+
|
67
|
+
puts " xmin: #{self.xmin}" if self.debug
|
68
|
+
|
69
|
+
self.xmin
|
70
|
+
end
|
71
|
+
|
72
|
+
def recalculate_xmax
|
73
|
+
puts "recalculate_xmax" if self.debug
|
74
|
+
|
75
|
+
self.xmax = self.graph_data.max_by{ |p| p.x.to_f }.x
|
76
|
+
|
77
|
+
puts " xmax: #{self.xmax}" if self.debug
|
78
|
+
|
79
|
+
self.xmax
|
80
|
+
end
|
81
|
+
|
82
|
+
def recalculate_ymin
|
83
|
+
puts "recalculate_ymin" if self.debug
|
84
|
+
|
85
|
+
self.ymin = self.graph_data.min_by{ |p| p.y.to_f }.y
|
86
|
+
end
|
87
|
+
|
88
|
+
def recalculate_ymax
|
89
|
+
puts "recalculate_ymax" if self.debug
|
90
|
+
|
91
|
+
self.ymax = self.graph_data.max_by{ |p| p.y.to_f }.y
|
92
|
+
end
|
93
|
+
|
94
|
+
def graph_width
|
95
|
+
width - left_padding - right_padding
|
96
|
+
end
|
97
|
+
|
98
|
+
def graph_height
|
99
|
+
height - bottom_padding - top_padding
|
100
|
+
end
|
101
|
+
|
102
|
+
def recalculate_pixels_per_unit
|
103
|
+
self.x_pixels_per_unit = graph_width / (xmax.to_f - xmin.to_f)
|
104
|
+
self.y_pixels_per_unit = graph_height / (ymax.to_f - ymin.to_f)
|
105
|
+
|
106
|
+
if xmax - xmin < (86400 * 3.5)
|
107
|
+
self.xticsmod = 3600
|
108
|
+
elsif xmax - xmin < (86400 * 7.5)
|
109
|
+
self.xticsmod = (86400 * 24)
|
110
|
+
elsif xmax - xmin < (86400 * 50)
|
111
|
+
self.xticsmod = (86400 * 24 * 7)
|
112
|
+
elsif xmax - xmin < (86400 * 800)
|
113
|
+
self.xticsmod = (86400 * 24 * 30)
|
114
|
+
else
|
115
|
+
self.xticsmod = 3600
|
116
|
+
end
|
117
|
+
|
118
|
+
# if options[:debug]
|
119
|
+
# puts "@x_pixels_per_unit: #{@x_pixels_per_unit}"
|
120
|
+
# puts "@y_pixels_per_unit: #{@y_pixels_per_unit}"
|
121
|
+
# end
|
122
|
+
end
|
123
|
+
|
124
|
+
def draw_graph( data )
|
125
|
+
self.graph_data = data
|
126
|
+
|
127
|
+
recalculate_pixels_per_unit
|
128
|
+
|
129
|
+
data.each_with_index do |p, i|
|
130
|
+
data[i] = translate_data_point_to_graph_point( p )
|
131
|
+
end
|
132
|
+
|
133
|
+
data.each_with_index do |point, i|
|
134
|
+
if i < data.size - 1
|
135
|
+
x1 = point.x
|
136
|
+
y1 = point.y
|
137
|
+
|
138
|
+
x2 = data[i+1].x
|
139
|
+
y2 = data[i+1].y
|
140
|
+
|
141
|
+
self.bitmap_drawing.draw_line( Point.new( x1, y1 ), Point.new( x2, y2), self.data_color )
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def draw_horizontal_gridlines
|
147
|
+
(self.ymin.to_i..self.ymax.to_i).each do |y|
|
148
|
+
if y % self.yticsmod == 0
|
149
|
+
p1 = translate_data_point_to_graph_point( Point.new( self.xmin, y ) )
|
150
|
+
p2 = translate_data_point_to_graph_point( Point.new( self.xmax, y ) )
|
151
|
+
|
152
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), [0xAA,0xAA,0xAA] )
|
153
|
+
|
154
|
+
self.bitmap_drawing.draw_text( "#{y}", Point.new( p1.x - (10 * y.to_s.size), p1.y ), [0x00,0x00,0x00] )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def draw_vertical_gridlines
|
160
|
+
(self.xmin.to_i..self.xmax.to_i).each do |x|
|
161
|
+
if x % self.xticsmod == 0
|
162
|
+
p1 = translate_data_point_to_graph_point( Point.new( x, self.ymin ) )
|
163
|
+
p2 = translate_data_point_to_graph_point( Point.new( x, self.ymax ) )
|
164
|
+
|
165
|
+
#Manual offset for GMT-7
|
166
|
+
if (x + (3600*-7)) % 86400 == 0
|
167
|
+
lcolor = [0x00,0x00,0xFF]
|
168
|
+
else
|
169
|
+
lcolor = [0xAA,0xAA,0xAA]
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), lcolor )
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def draw_graph_outline
|
179
|
+
p1 = translate_data_point_to_graph_point( Point.new( self.xmin, self.ymin ) )
|
180
|
+
p2 = translate_data_point_to_graph_point( Point.new( self.xmax, self.ymin ) )
|
181
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), [0x22,0x22,0x22] )
|
182
|
+
|
183
|
+
p1 = translate_data_point_to_graph_point( Point.new( self.xmax, self.ymin ) )
|
184
|
+
p2 = translate_data_point_to_graph_point( Point.new( self.xmax, self.ymax ) )
|
185
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), [0x22,0x22,0x22] )
|
186
|
+
|
187
|
+
p1 = translate_data_point_to_graph_point( Point.new( self.xmax, self.ymax ) )
|
188
|
+
p2 = translate_data_point_to_graph_point( Point.new( self.xmin, self.ymax ) )
|
189
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), [0x22,0x22,0x22] )
|
190
|
+
|
191
|
+
p1 = translate_data_point_to_graph_point( Point.new( self.xmin, self.ymax ) )
|
192
|
+
p2 = translate_data_point_to_graph_point( Point.new( self.xmin, self.ymin ) )
|
193
|
+
self.bitmap_drawing.draw_line( Point.new( p1.x, p1.y ), Point.new( p2.x, p2.y ), [0x22,0x22,0x22] )
|
194
|
+
end
|
195
|
+
|
196
|
+
def draw_xaxis_labels
|
197
|
+
(self.xmin.to_i..self.xmax.to_i).each do |x|
|
198
|
+
|
199
|
+
if x % 3600 == 0
|
200
|
+
x_offset = translate_data_point_to_graph_point( Point.new( x, 10 ) ).x.to_i
|
201
|
+
|
202
|
+
h = Time.at(x).hour
|
203
|
+
|
204
|
+
if h % 2 == 0
|
205
|
+
if self.debug
|
206
|
+
puts "h: #{h.to_s}, x: #{x_offset}"
|
207
|
+
end
|
208
|
+
self.bitmap_drawing.draw_text( h.to_s, Point.new( x_offset, self.height - 15 ), [0x00,0x00,0x00] )
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def draw_yaxis_labels
|
215
|
+
# Currently happening in the y gridlines method
|
216
|
+
end
|
217
|
+
|
218
|
+
def translate_data_point_to_graph_point( point )
|
219
|
+
|
220
|
+
if point.x < self.xmin
|
221
|
+
point.x = self.xmin
|
222
|
+
elsif point.x > self.xmax
|
223
|
+
point.x = self.xmax
|
224
|
+
end
|
225
|
+
|
226
|
+
if point.y < self.ymin
|
227
|
+
point.y = self.ymin
|
228
|
+
elsif point.y > self.ymax
|
229
|
+
point.y = self.ymax
|
230
|
+
end
|
231
|
+
|
232
|
+
#puts "Mid x: #{point.x}, y: #{point.y}"
|
233
|
+
|
234
|
+
x = self.left_padding + (point.x - self.xmin) * self.x_pixels_per_unit
|
235
|
+
y = self.height - self.bottom_padding - ((point.y - self.ymin) * self.y_pixels_per_unit)
|
236
|
+
|
237
|
+
#puts "Out x: #{x}, y: #{y}"
|
238
|
+
|
239
|
+
return Point.new( x, y )
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
data/lib/graphit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jeffmcfadden
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bin_utils
|
@@ -72,7 +72,12 @@ files:
|
|
72
72
|
- graphit.gemspec
|
73
73
|
- graphit/version.rb
|
74
74
|
- lib/graphit.rb
|
75
|
+
- lib/graphit/bitmap_drawing.rb
|
76
|
+
- lib/graphit/bmp_file.rb
|
77
|
+
- lib/graphit/color.rb
|
78
|
+
- lib/graphit/graph.rb
|
75
79
|
- lib/graphit/pixel_font.rb
|
80
|
+
- lib/graphit/point.rb
|
76
81
|
- lib/graphit/version.rb
|
77
82
|
- test-01.bmp
|
78
83
|
- test-01.png
|