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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(technical_graph) ⇒ GraphImageDrawer



84
85
86
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
# File 'lib/technical_graph/graph_image_drawer.rb', line 84

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.



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

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

Instance Method Details

#antialiasObject



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

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



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

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



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

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



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

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



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

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
# File 'lib/technical_graph/graph_image_drawer.rb', line 19

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

  if options[:drawer_class] == :rasem
    require 'technical_graph/graph_image_drawer_rasem'
    return GraphImageDrawerRasem
  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



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

def height
  options[:height].to_i
end

#height=(h) ⇒ Object



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

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



162
163
164
# File 'lib/technical_graph/graph_image_drawer.rb', line 162

def legend_auto_position
  options[:legend_auto]
end

#legend_heightObject



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

def legend_height
  options[:legend_height]
end

#legend_marginObject



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

def legend_margin
  options[:legend_margin]
end

#legend_widthObject



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

def legend_width
  options[:legend_width]
end

#legend_xObject



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

def legend_x
  options[:legend_x]
end

#legend_yObject



158
159
160
# File 'lib/technical_graph/graph_image_drawer.rb', line 158

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



256
257
258
# File 'lib/technical_graph/graph_image_drawer.rb', line 256

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



247
248
249
250
251
# File 'lib/technical_graph/graph_image_drawer.rb', line 247

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



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

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



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/technical_graph/graph_image_drawer.rb', line 271

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



261
262
263
264
265
266
267
268
# File 'lib/technical_graph/graph_image_drawer.rb', line 261

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



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/technical_graph/graph_image_drawer.rb', line 212

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



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/technical_graph/graph_image_drawer.rb', line 317

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

#rmagick_installed?Boolean

Check if rmagick is installed



36
37
38
# File 'lib/technical_graph/graph_image_drawer.rb', line 36

def rmagick_installed?
  return Gem.source_index.find_name('rmagick').size > 0
end

#save_to_file(file) ⇒ Object

Save output to file



342
343
344
345
346
347
348
349
# File 'lib/technical_graph/graph_image_drawer.rb', line 342

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



352
353
354
355
356
357
358
359
360
361
# File 'lib/technical_graph/graph_image_drawer.rb', line 352

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



364
365
366
# File 'lib/technical_graph/graph_image_drawer.rb', line 364

def to_png
  drawer.to_png
end

#to_svgObject



368
369
370
# File 'lib/technical_graph/graph_image_drawer.rb', line 368

def to_svg
  drawer.to_svg
end

#to_svgzObject



372
373
374
# File 'lib/technical_graph/graph_image_drawer.rb', line 372

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



130
131
132
# File 'lib/technical_graph/graph_image_drawer.rb', line 130

def width
  options[:width].to_i
end

#width=(w) ⇒ Object



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

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