Method: PDF::Charts::StdDev#render_on

Defined in:
lib/extensions/pdf-writer/pdf/charts/stddev.rb

#render_on(pdf) ⇒ Object

Draw the standard deviation chart on the supplied PDF document.

Raises:

  • (TypeError)


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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/extensions/pdf-writer/pdf/charts/stddev.rb', line 219

def render_on(pdf)
  raise TypeError, PDF::Writer::Lang[:charts_stddev_data_empty] if @data.empty?
  data = @data.dup
  leftover_data = nil

  loop do
    # Set up the scale information.
    scale = []

    (@scale.first + @scale.step).step(@scale.last, @scale.step) do |ii|
      scale << "%01.#{@scale.label.decimal_precision}f" % ii
    end

    scales = PDF::Writer::OHash.new
    scale.each_with_index do |gg, ii|
      scales[ii] = OpenStruct.new
      scales[ii].value = gg
    end

    # Add information about the scales' locations to the scales
    # hash. Note that the count is one smaller than it should be, so we're
    # increasing it. The first scale is the bottom of the chart.
    scale_count = scale.size + 1

    label_height_adjuster = 0
    label_height_adjuster = @label.height if @show_labels

    chart_area_height = @height - label_height_adjuster
    scale_height   = chart_area_height / scale_count.to_f

    scales.each_key do |index|
      this_height = scale_height * (index + 1) + @label.height
      scales[index].line_height = this_height
      if @scale.show_labels
        scales[index].label_height = this_height -
        (@scale.label.text_size / 3.0)
      end
    end

    # How many sections do we need in this chart, and how wide will it
    # need to be?
    chunk_width = @datapoint_width
    num_chunks  = data.size
    widest_scale_label = 0

    if @scale.show_labels
      scales.each_value do |scale|
        this_width = pdf.text_width(scale.value, @scale.label.text_size)
        widest_scale_label = this_width if this_width > widest_scale_label
      end
    end

    chart_width = chunk_width * num_chunks
    total_width = chart_width + widest_scale_label + @scale.label.pad

      # What happens if the projected width of the chart is too big?
      # Figure out how to break the chart in pieces.
    if total_width > @maximum_width
      max_column_count = 0
      base_width = widest_scale_label + @scale.label.pad
      (1..(num_chunks + 1)).each do |ii|
        if (base_width + (ii * chunk_width)) > @maximum_width
          break
        else
          max_column_count += 1
        end
      end

      leftover_data = data.slice!(max_column_count, -1)

      num_chunks  = data.size
      chart_width = chunk_width * num_chunks
      total_width = chart_width + widest_scale_label + @scale.label.pad
    end

    chart_y = pdf.y - @height + @leading_gap
    chart_y += (@outer_borders.style.width * 2.0) if @outer_borders

    if chart_y < pdf.bottom_margin
      pdf.start_new_page
      chart_y = pdf.y - @height
      chart_y += (@outer_borders.style.width * 2.0) if @outer_borders
    end

    chart_x = pdf.absolute_x_middle - (total_width / 2.0) + widest_scale_label

      # Add labels, if needed.
    if @show_labels
      pdf.save_state
      pdf.fill_color! @label.background_color
      # Draw a rectangle for each label
      num_chunks.times do |ii|
        this_x = chart_x + ii * chunk_width
        pdf.rectangle(this_x, chart_y, chunk_width, @label.height).fill
      end

        # Add a border above the label rectangle.
      if @outer_borders
        pdf.stroke_style! @outer_borders.style
        pdf.line(chart_x, chart_y + @label.height, chart_x + chart_width, chart_y + @label.height).stroke
      end
      pdf.fill_color! @label.text_color

      data.each_with_index do |datum, ii|
        label = datum.label.to_s
        label_width = pdf.text_width(label, @label.text_size)
        this_x = chart_x + (ii * chunk_width) + (chunk_width / 2.0) - (label_width / 2.0)
        this_y = chart_y + (@label.height / 2.0) - (@label.text_size / 3.0)
        pdf.add_text(this_x, this_y, label, @label.text_size)
      end
      pdf.restore_state
    end

    if @inner_borders
      pdf.save_state
      pdf.stroke_color! @inner_borders.color
      pdf.stroke_style! @inner_borders.style
      (num_chunks - 1).times do |ii|
        this_x = chart_x + (ii * chunk_width) + chunk_width
        pdf.line(this_x, chart_y, this_x, chart_y + @height).stroke
      end
      pdf.restore_state
    end

    pdf.save_state
    if @outer_borders
      pdf.stroke_color! @outer_borders.color
      pdf.stroke_style! @outer_borders.style
      pdf.rectangle(chart_x, chart_y, chart_width, @height).stroke
    end

    if @scale.style
      pdf.save_state
      pdf.stroke_style! @scale.style
      scales.each_value do |scale|
        this_y = chart_y + scale.line_height
        pdf.line(chart_x, this_y, chart_x + chart_width, this_y).stroke
      end
      pdf.restore_state
    end

    if @scale.show_labels
      pdf.save_state
      scales.each_value do |scale|
        this_y = chart_y + scale.label_height
        label_width = pdf.text_width(scale.value, @scale.label.text_size)
        this_x = chart_x - label_width - @scale.label.pad
        pdf.fill_color! @scale.label.text_color
        pdf.add_text(this_x, this_y, scale.value, @scale.label.text_size)
      end
      pdf.restore_state
    end

    data.each_with_index do |datum, ii|
      avg_height    = datum.average * scale_height
      stddev_height = datum.stddev * scale_height
      this_y        = chart_y + label_height_adjuster + avg_height
      this_x        = chart_x + (ii * chunk_width) + (chunk_width / 2.0)
      line_top_y    = this_y + (stddev_height / 2.0)
      line_bot_y    = this_y - (stddev_height / 2.0)

        # Plot the dot
      if @dot
        pdf.stroke_color! @dot.color
        pdf.stroke_style! @dot.style
        pdf.circle_at(this_x, this_y, (@dot.style.width / 2.0)).fill
      end

        # Plot the bar
      if @bar
        pdf.stroke_color! @bar.color
        pdf.stroke_style! @bar.style
        pdf.line(this_x, line_top_y, this_x, line_bot_y).stroke
      end

        # Plot the crossbars
      if @upper_crossbar
        if @dot
          cb_width = @dot.style.width
        else
          cb_width = @upper_crossbar.style.width
        end
        pdf.stroke_color! @upper_crossbar.color
        pdf.stroke_style! @upper_crossbar.style
        pdf.line(this_x - cb_width, line_top_y, this_x + cb_width, line_top_y).stroke
      end
      if @lower_crossbar
        if @dot
          cb_width = @dot.style.width
        else
          cb_width = @lower_crossbar.style.width
        end
        pdf.stroke_color! @lower_crossbar.color
        pdf.stroke_style! @lower_crossbar.style

        pdf.line(this_x - cb_width, line_bot_y, this_x + cb_width, line_bot_y).stroke
      end
    end

    pdf.restore_state

    pdf.y = chart_y

    break if leftover_data.nil?

    data = leftover_data
    leftover_data = nil
  end

  pdf.y
end