Class: GraphImageDrawer

Inherits:
Object
  • Object
show all
Defined in:
lib/technical_graph/graph_image_drawer.rb

Overview

options parameters: :width - width of image :height - height of image :x_min, :x_max, :y_min, :y_max - default or fixed ranges :xy_behaviour:

  • :default - use them as default ranges

  • :fixed - ranges will not be changed during addition of layers

Constant Summary collapse

DEFAULT_WIDTH =

default sizes

1600
DEFAULT_HEIGHT =
1200
ONE_LAYER_LEGEND_SPACER =

height of 1 layer without font size

5
@@default_width =
DEFAULT_WIDTH
@@default_height =
DEFAULT_HEIGHT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(technical_graph) ⇒ GraphImageDrawer

Returns a new instance of GraphImageDrawer.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/technical_graph/graph_image_drawer.rb', line 87

def initialize(technical_graph)
  @technical_graph = technical_graph

  t = Time.now

  # drawer type
  #options[:drawer_class] ||= :rmagick
  options[:drawer_class] ||= :rasem

  options[:width] ||= @@default_width
  options[:height] ||= @@default_height

  options[:axis_value_and_param_labels] = true if options[:axis_value_and_param_labels].nil?
  options[:axis_zero_labels] = true if options[:axis_zero_labels].nil?

  options[:adjust_axis_to_zero] = true if options[:adjust_axis_to_zero].nil?

  # colors
  options[:background_color] ||= 'white'
  options[:background_hatch_color] ||= 'lightcyan2'
  options[:axis_color] ||= '#000000'

  # antialias
  options[:antialias] = false if options[:antialias].nil?

  # font sizes
  options[:axis_font_size] ||= 10
  options[:layers_font_size] ||= 10
  options[:axis_label_font_size] ||= 10
  options[:legend_font_size] ||= 10

  # legend
  options[:legend] = false if options[:legend].nil?
  options[:legend_auto] = true if options[:legend_auto].nil?
  options[:legend_x] ||= 50
  options[:legend_y] ||= 50
  options[:legend_width] ||= 100
  options[:legend_margin] ||= 50

  # array of all points drawn on graph, used for auto positioning of legend
  @drawn_points = Array.new

  logger.debug "initializing #{self.class}"
  logger.debug " TIME COST #{Time.now - t}"
end

Instance Attribute Details

#drawerObject (readonly)

Returns the value of attribute drawer.



232
233
234
# File 'lib/technical_graph/graph_image_drawer.rb', line 232

def drawer
  @drawer
end

#technical_graphObject (readonly)

Returns the value of attribute technical_graph.



45
46
47
# File 'lib/technical_graph/graph_image_drawer.rb', line 45

def technical_graph
  @technical_graph
end

Class Method Details

.heightObject

Get default graph height



155
156
157
# File 'lib/technical_graph/graph_image_drawer.rb', line 155

def self.height
  @@default_height
end

.height=(h) ⇒ Object

Set default graph height



165
166
167
# File 'lib/technical_graph/graph_image_drawer.rb', line 165

def self.height=(h)
  @@default_height = h.to_i if h.to_i > 0
end

.widthObject

Get default graph width



150
151
152
# File 'lib/technical_graph/graph_image_drawer.rb', line 150

def self.width
  @@default_width
end

.width=(w) ⇒ Object

Set default graph width



160
161
162
# File 'lib/technical_graph/graph_image_drawer.rb', line 160

def self.width=(w)
  @@default_width = w.to_i if w.to_i > 0
end

Instance Method Details

#antialiasObject



169
170
171
# File 'lib/technical_graph/graph_image_drawer.rb', line 169

def antialias
  options[:antialias] == true
end

#axis_font_sizeObject



75
76
77
# File 'lib/technical_graph/graph_image_drawer.rb', line 75

def axis_font_size
  options[:axis_font_size]
end

#best_output_formatObject

Best output image format, used for testing



41
42
43
# File 'lib/technical_graph/graph_image_drawer.rb', line 41

def best_output_format
  @technical_graph.best_output_format
end

#calc_bitmap_x(_x) ⇒ Object

Calculate image X position



202
203
204
205
206
# File 'lib/technical_graph/graph_image_drawer.rb', line 202

def calc_bitmap_x(_x)
  l = data_processor.x_max - data_processor.x_min
  offset = _x - data_processor.x_min
  return (offset.to_f * width.to_f) / l.to_f
end

#calc_bitmap_y(_y) ⇒ Object

Calculate image Y position



209
210
211
212
213
214
# File 'lib/technical_graph/graph_image_drawer.rb', line 209

def calc_bitmap_y(_y)
  l = data_processor.y_max - data_processor.y_min
  #offset = _y - data_processor.y_min
  offset = data_processor.y_max - _y
  return (offset.to_f * height.to_f) / l.to_f
end

#crate_blank_graph_imageObject

Create background image



223
224
225
226
227
228
229
230
# File 'lib/technical_graph/graph_image_drawer.rb', line 223

def crate_blank_graph_image
  # reset color banks
  GraphColorLibrary.instance.reset
  # calculate some stuff :]
  pre_image_create_calculations
  # create drawing proxy
  @drawer = drawing_class.new(self)
end

#data_processorObject

Calculate everything



58
59
60
# File 'lib/technical_graph/graph_image_drawer.rb', line 58

def data_processor
  @technical_graph.data_processor
end

#draw_legend?Boolean

Returns:

  • (Boolean)


173
174
175
# File 'lib/technical_graph/graph_image_drawer.rb', line 173

def draw_legend?
  options[:legend]
end

#drawing_classObject

Which type of drawing class use?



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/technical_graph/graph_image_drawer.rb', line 19

def drawing_class
  if options[:drawer_class] == :rasem
    require 'technical_graph/graph_image_drawer_rasem'
    return GraphImageDrawerRasem
  end

  if options[:drawer_class] == :chunky_png
    require 'technical_graph/graph_image_drawer_chunky'
    return GraphImageDrawerChunky
  end

  if options[:drawer_class] == :rmagick and rmagick_installed?
    require 'technical_graph/graph_image_drawer_rmagick'
    return GraphImageDrawerRmagick
  end

  # default
  require 'technical_graph/graph_image_drawer_rasem'
  return GraphImageDrawerRasem
end

#graph_axisObject

Axis processing



63
64
65
# File 'lib/technical_graph/graph_image_drawer.rb', line 63

def graph_axis
  @technical_graph.axis
end

#heightObject



137
138
139
# File 'lib/technical_graph/graph_image_drawer.rb', line 137

def height
  options[:height].to_i
end

#height=(h) ⇒ Object



145
146
147
# File 'lib/technical_graph/graph_image_drawer.rb', line 145

def height=(h)
  options[:height] = h.to_i if h.to_i > 0
end

#layersObject

Accessor for DataLayer Array



53
54
55
# File 'lib/technical_graph/graph_image_drawer.rb', line 53

def layers
  @technical_graph.layers
end

#legend_auto_positionObject



185
186
187
# File 'lib/technical_graph/graph_image_drawer.rb', line 185

def legend_auto_position
  options[:legend_auto]
end

#legend_heightObject



193
194
195
# File 'lib/technical_graph/graph_image_drawer.rb', line 193

def legend_height
  options[:legend_height]
end

#legend_marginObject



197
198
199
# File 'lib/technical_graph/graph_image_drawer.rb', line 197

def legend_margin
  options[:legend_margin]
end

#legend_widthObject



189
190
191
# File 'lib/technical_graph/graph_image_drawer.rb', line 189

def legend_width
  options[:legend_width]
end

#legend_xObject



177
178
179
# File 'lib/technical_graph/graph_image_drawer.rb', line 177

def legend_x
  options[:legend_x]
end

#legend_yObject



181
182
183
# File 'lib/technical_graph/graph_image_drawer.rb', line 181

def legend_y
  options[:legend_y]
end

#loggerObject



67
68
69
# File 'lib/technical_graph/graph_image_drawer.rb', line 67

def logger
  @technical_graph.logger
end

#one_layer_legend_heightObject



279
280
281
# File 'lib/technical_graph/graph_image_drawer.rb', line 279

def one_layer_legend_height
  options[:legend_font_size] + ONE_LAYER_LEGEND_SPACER
end

#optionsObject

Accessor for options Hash



48
49
50
# File 'lib/technical_graph/graph_image_drawer.rb', line 48

def options
  @technical_graph.options
end

#post_dot_drawn(bx, by) ⇒ Object

Used for auto position for legend



270
271
272
273
274
# File 'lib/technical_graph/graph_image_drawer.rb', line 270

def post_dot_drawn(bx, by)
  if legend_auto_position
    @drawn_points << { :x => bx, :y => by }
  end
end

#pre_image_create_calculationsObject

Everything that must be done before creating image



217
218
219
220
# File 'lib/technical_graph/graph_image_drawer.rb', line 217

def pre_image_create_calculations
  # check axis density and enlarge if this option is on
  graph_axis.axis_distance_image_enlarge
end

#recalculate_legend_positionObject

Choose best location



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/technical_graph/graph_image_drawer.rb', line 294

def recalculate_legend_position
  return unless legend_auto_position
  logger.debug "Auto position calculation, drawn points #{@drawn_points.size}"

  # check 8 places:
  positions = [
    { :x => legend_margin, :y => 0 + legend_margin }, # top-left
    { :x => width/2, :y => 0 + legend_margin }, # top-center
    { :x => width - legend_margin - legend_width, :y => 0 + legend_margin }, # top-right
    { :x => legend_margin, :y => height/2 }, # middle-left
    { :x => width - legend_margin - legend_width, :y => height/2 }, # middle-right
    { :x => legend_margin, :y => height - legend_margin - legend_height }, # bottom-left
    { :x => width/2, :y => height - legend_margin - legend_height }, # bottom-center
    { :x => width - legend_margin - legend_width, :y => height - legend_margin - legend_height }, # bottom-right
  ]

  t = Time.now

  # calculate nearest distance of all drawn points
  positions.each do |p|
    p[:distance] = (width ** 2 + height ** 2) ** 0.5 # max distance, diagonal of graph
    @drawn_points.each do |dp|
      # calculate drawn point distance to being checked now legend position
      two_points_distance = ((p[:x] - dp[:x]) ** 2 + (p[:y] - dp[:y]) ** 2) ** 0.5
      # modify only if distance is closer
      if p[:distance] > two_points_distance
        p[:distance] = two_points_distance
      end
    end
  end

  logger.debug "auto legend best position distance calc."
  logger.debug " TIME COST #{Time.now - t}"
  t = Time.now

  # chose position with highest distance
  positions.sort! { |a, b| a[:distance] <=> b[:distance] }
  best_position = positions.last
  options[:legend_x] = best_position[:x]
  options[:legend_y] = best_position[:y]

  logger.debug "Best position x #{options[:legend_x]}, y #{options[:legend_y]}, distance #{best_position[:distance]}"
  logger.debug " TIME COST #{Time.now - t}"
end

#recalculate_legend_sizeObject

Enlarge legend’s width using legend labels sizes



284
285
286
287
288
289
290
291
# File 'lib/technical_graph/graph_image_drawer.rb', line 284

def recalculate_legend_size
  layers.each do |l|
    w = l.label.size * options[:legend_font_size]
    options[:legend_width] = w if w > legend_width
  end

  options[:legend_height] = layers.size * one_layer_legend_height
end

#render_data_layer(l) ⇒ Object

Render data layer, calculate coords and execute proxy



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/technical_graph/graph_image_drawer.rb', line 235

def render_data_layer(l)
  layer_data = l.processed_data

  t = Time.now

  # calculate coords, draw text, and then lines and circles
  coords = Array.new

  (0...(layer_data.size - 1)).each do |i|
    ax = layer_data[i].x
    ax = calc_bitmap_x(ax).round
    ay = layer_data[i].y
    ay = calc_bitmap_y(ay).round

    bx = layer_data[i+1].x
    bx = calc_bitmap_x(bx).round
    by = layer_data[i+1].y
    by = calc_bitmap_y(by).round

    coords << {
      :ax => ax, :ay => ay,
      :bx => bx, :by => by,
      :dy => layer_data[i].y
    }
  end

  logger.debug "rendering layer of size #{layer_data.size}, bitmap position calculation"
  logger.debug " TIME COST #{Time.now - t}"
  t = Time.now

  # draw using proxy
  drawer.render_data_layer(l, coords)
end

#render_data_legendObject

Render legend on graph



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/technical_graph/graph_image_drawer.rb', line 340

def render_data_legend
  return unless draw_legend?
  recalculate_legend_size
  recalculate_legend_position

  x = legend_x
  y = legend_y

  legend_data = Array.new

  layers.each do |l|
    h = Hash.new
    h[:color] = l.color
    h[:label] = l.label
    h[:x] = x
    h[:y] = y

    legend_data << h
    y += one_layer_legend_height
  end

  drawer.legend(legend_data)
end

#save_to_file(file) ⇒ Object

Save output to file



365
366
367
368
369
370
371
372
# File 'lib/technical_graph/graph_image_drawer.rb', line 365

def save_to_file(file)
  t = Time.now

  drawer.save(file)

  logger.debug "saving image"
  logger.debug " TIME COST #{Time.now - t}"
end

#to_format(format) ⇒ Object

Export image



375
376
377
378
379
380
381
382
383
384
# File 'lib/technical_graph/graph_image_drawer.rb', line 375

def to_format(format)
  t = Time.now

  blob = drawer.to_format(format)

  logger.debug "exporting image as string"
  logger.debug " TIME COST #{Time.now - t}"

  return blob
end

#to_pngObject

Return binary PNG



387
388
389
# File 'lib/technical_graph/graph_image_drawer.rb', line 387

def to_png
  drawer.to_png
end

#to_svgObject



391
392
393
# File 'lib/technical_graph/graph_image_drawer.rb', line 391

def to_svg
  drawer.to_svg
end

#to_svgzObject



395
396
397
# File 'lib/technical_graph/graph_image_drawer.rb', line 395

def to_svgz
  drawer.to_svgz
end

#truncate_stringObject



71
72
73
# File 'lib/technical_graph/graph_image_drawer.rb', line 71

def truncate_string
  options[:truncate_string]
end

#widthObject



133
134
135
# File 'lib/technical_graph/graph_image_drawer.rb', line 133

def width
  options[:width].to_i
end

#width=(w) ⇒ Object



141
142
143
# File 'lib/technical_graph/graph_image_drawer.rb', line 141

def width=(w)
  options[:width] = w.to_i if w.to_i > 0
end