rubylabs 0.6.4 → 0.7.0

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.4
1
+ 0.7.0
data/lib/randomlab.rb CHANGED
@@ -274,255 +274,174 @@ module RandomLab
274
274
  end # class Card
275
275
 
276
276
  =begin rdoc
277
- Visualization helpers (private):
278
- make_canvas create a new blank canvas (same one used for all tests)
279
277
  Visualization methods called by students to test a PRNG object:
280
- view(type,*args) initialize canvas for a visualization of the specified type
281
- tick_mark(i) add a tick mark at location i on the number line
282
- State vars maintained by the visualization methods:
283
- @@drawing initially nil, set to a Draw object when canvas has been created
284
- @@tkroot top level Tk application that manages the canvas (should never be used)
285
- @@visual set to :line, :histogram, or :dot_plot when drawing initialized
286
- @@endpoint last point on number line (value of n passed to init_line)
278
+
279
+ view_numberline(n) make a number line for integers between 0 and n-1
280
+ tick_mark(i) draw a tick mark at location i on the number line
281
+
282
+ view_histogram(n, max) make a histogram with n bins for value from 0 to max
283
+ view_histogram(a) make a histogram with one bin for each item in a
284
+ update_bin(x) add 1 to the count of items in bin x
285
+ get_counts get a copy of the bins (array of counts)
286
+
287
+ view_dotplot(n) initialize an n x n dotplot
288
+ plot_point(x,y) add a dot at (x,y) to the dotplot
287
289
  =end
288
290
 
289
- def make_canvas
290
- require 'tk'
291
-
292
- if ! defined? @@tkroot
293
- @@tkroot = TkRoot.new { title "RandomLab" }
294
- end
295
-
296
- if @@drawing == nil
297
- @@drawing = Draw.new(@tkroot)
298
- @@threads = []
299
- @@threads << Thread.new() do
300
- Tk.mainloop
301
- end
302
- end
291
+ def view_numberline(npoints, userOptions = {})
292
+ Canvas.init(500, 100, "RandomLab::NumberLine")
293
+ options = @@numberLineOptions.merge(userOptions)
294
+ line = Canvas.line(0, 70, 500, 70, :width => options[:lineThickness], :fill => options[:lineColor])
295
+ @@drawing = NumberLine.new(line, npoints, options)
296
+ return true
303
297
  end
304
298
 
305
- def view(*args)
306
- kind = args.shift
307
- if ! @@views.include?(kind)
308
- puts "view type must be one of #{@@views.join(', ')}"
309
- else
310
- @@visual = kind
311
- case kind
312
- when :line
313
- @@npoints = args[0]
314
- if @@npoints.kind_of? Numeric
315
- make_canvas if @@drawing.nil?
316
- @@drawing.new_line(@@npoints)
317
- else
318
- puts "usage: view(:line, npoints)"
319
- end
320
- when :dotplot
321
- @@max = args[0]
322
- if @@max.kind_of? Numeric
323
- make_canvas if @@drawing.nil?
324
- @@drawing.new_dotplot(@@max)
325
- else
326
- puts "usage: view(:dotplot, max)"
327
- end
328
- when :histogram
329
- if args[0].class == Array
330
- @@keys = args[0]
331
- @@nbins = @@max = @@keys.length
332
- @@counts = Hash.new
333
- @@keys.each { |k| @@counts[k] = 0 }
334
- else
335
- @@nbins, @@max = args
336
- @@max = @@nbins if @@max.nil?
337
- @@keys = nil
338
- end
339
- if [@@nbins, @@max].all? { |x| x.kind_of? Numeric }
340
- make_canvas if @@drawing.nil?
341
- @@drawing.new_histogram(@@nbins, @@max)
342
- else
343
- puts "usage: view(:histogram, nbins, max) or view(:histogram, keys)"
344
- end
345
- end
346
- end
347
- end
348
-
349
299
  def tick_mark(i)
350
- if @@visual != :line
351
- puts "call 'view(:line,n)' to make a number line of size n'"
352
- elsif i < 0 || i >= @@npoints
353
- puts "tick_mark: 0 <= i < #{@@npoints}"
300
+ if @@drawing.class != NumberLine
301
+ puts "call view_numberline to initialize the number line"
302
+ elsif i < 0 || i >= @@drawing.npoints
303
+ puts "tick_mark: 0 <= i < #{@@drawing.npoints}"
354
304
  else
355
- @@drawing.add_tick(i)
305
+ x0, y0, x1, y1 = @@drawing.line.coords
306
+ tx = (i.to_f / @@drawing.npoints) * (x1-x0)
307
+ ty = y0 - @@drawing.options[:tickHeight]
308
+ Canvas.line(tx, y0, tx, ty, :width => @@drawing.options[:tickWidth], :fill => @@drawing.options[:tickColor])
356
309
  sleep(@@delay)
357
310
  end
358
- return nil
311
+ return true
359
312
  end
360
-
361
- def update_bin(x)
362
- if @@visual != :histogram
363
- puts "call view(:histogram, n, max) to initialize a histogram with n bins"
364
- else
365
- i = @@keys ? @@keys.index(x) : x
366
- if i.nil?
367
- puts "unknown item: #{x}"
368
- elsif i < 0 || i >= @@max
369
- puts "count: 0 <= i < #{@@max}"
313
+
314
+ def view_histogram(*args)
315
+ begin
316
+ if args[0].class == Array
317
+ userOptions = args.length > 1 ? args[1] : { }
318
+ raise "usage: view_histogram(keys, options)" unless userOptions.class == Hash
319
+ keys = args[0]
320
+ nbins = max = keys.length
370
321
  else
371
- @@drawing.add_hist(i)
372
- @@counts[x] += 1 if @@keys
373
- sleep(@@delay)
374
- end
322
+ userOptions = args.length > 2 ? args[2] : { }
323
+ raise "usage: view_histogram(nbins, max, options)" unless userOptions.class == Hash
324
+ nbins = args[0]
325
+ max = args.length > 1 ? args[1] : nbins
326
+ keys = nil
327
+ end
328
+ rescue Exception => e
329
+ puts e
330
+ return false
331
+ end
332
+
333
+ Canvas.init(500, 300, "RandomLab::Histogram")
334
+ counts = Hash.new(0)
335
+ options = @@histogramOptions.merge(userOptions)
336
+ bins = []
337
+ binHeight = 3
338
+ binBorder = 2
339
+ binWidth = (500/(nbins+1))
340
+ binTop = 280
341
+ nbins.times do |i|
342
+ x = i * binWidth + binWidth/2
343
+ bins << Canvas.rectangle( x + binBorder, binTop, x + binWidth - binBorder, binTop + binHeight, :outline => options[:binColor], :fill => options[:binColor] )
375
344
  end
376
- return nil
377
- end
378
345
 
379
- def get_counts
380
- return @@counts
381
- end
346
+ @@drawing = Histogram.new(bins, max.to_f, keys, counts, binTop, options)
382
347
 
383
- def plot_point(x,y)
384
- if @@visual != :dotplot
385
- puts "call view(:dotplot, max) to initialize a dot plot"
386
- elsif x < 0 || x >= @@max || y < 0 || y >= @@max
387
- puts "plot_point: 0 <= x, y < #{@@max}"
388
- else
389
- @@drawing.add_point(x,y)
390
- sleep(@@delay)
391
- end
392
- return nil
348
+ return true
393
349
  end
394
-
395
-
396
- =begin rdoc
397
- Make a window to display dot plots and histograms
398
- =end
399
350
 
400
- class Draw
401
-
402
- attr_accessor :canvas, :ticks, :bins
403
-
404
- @@canvasWidth = @@canvasHeight = 500
405
- @@tickHeight = 20
406
- @@tickWidth = 1
407
- @@traceSize = 10
408
- @@tickColor = 'blue'
409
- @@histColor = '#000080'
410
- @@dotSize = 2
411
- @@dotColor = '#000080'
412
-
413
- def new_line(nticks)
414
- self.erase_objects
415
- @title = TkcText.new(@@drawing.canvas, 100, 10, :text => "Number Line")
416
- @endpoint = nticks
417
- y = @@canvasHeight / 2
418
- @line = TkcLine.new( @canvas, 0, y, @@canvasWidth, y, :width => 3, :fill => '#777777' )
419
- return nil
420
- end
421
-
422
- def add_tick(i)
423
- x = (i.to_f / @endpoint) * @@canvasWidth
424
- y = @@canvasHeight / 2
425
- @ticks << TkcLine.new( @canvas, x, y, x, y-@@tickHeight, :width => @@tickWidth, :fill => @@tickColor )
426
- @ticks.last(@@traceSize).each_with_index { |t,i| t['fill'] = @palette[i] }
427
- end
428
351
 
429
- def new_histogram(nbins, max)
430
- self.erase_objects
431
- @title = TkcText.new(@@drawing.canvas, 100, 10, :text => "Histogram")
432
- @nbins, @max = nbins, max
433
- @baseline = @@canvasHeight / 2
434
- w = @@canvasWidth / @nbins
435
- @nbins.times do |i|
436
- x = i * w
437
- @bins[i] = TkcRectangle.new( @canvas, x, @baseline, x+w, @baseline-3, :outline => "#CCCCCC", :fill => @@histColor )
438
- end
439
- @dy = 8.0
440
- @yt = 50
352
+ def update_bin(x)
353
+ if @@drawing.class != Histogram
354
+ puts "call view_histogram to initialize a histogram"
441
355
  return nil
442
356
  end
443
-
444
- def add_hist(x)
445
- i = (x.to_f / @max) * @nbins
446
- rect = @bins[i]
447
- x1, y1, x2, y2 = rect.coords
448
- y1 = y1 - @dy
449
- rect.coords = [x1, y1, x2, y2]
450
- if y1 < @yt
451
- @bins.each do |rect|
452
- x1, y1, x2, y2 = rect.coords
453
- y1 = @baseline - ((@baseline - y1) / 2)
454
- rect.coords = [x1, y1, x2, y2]
455
- end
456
- @dy = @dy / 2
357
+ if @@drawing.keys
358
+ i = @@drawing.keys.index(x)
359
+ if i.nil?
360
+ puts "unknown bin: #{x}"
361
+ return nil
457
362
  end
363
+ else
364
+ xmax = @@drawing.max
365
+ nb = @@drawing.bins.length
366
+ if x < 0 || x >= xmax
367
+ puts "x must be between 0 and #{xmax-1}"
368
+ return nil
369
+ end
370
+ i = ((x / xmax) * nb).to_i
458
371
  end
459
-
460
- def new_dotplot(n)
461
- self.erase_objects
462
- @title = TkcText.new(@@drawing.canvas, 100, 10, :text => "Dot Plot")
463
- @max = n.to_f
372
+ @@drawing.counts[i] += 1
373
+ rect = @@drawing.bins[i]
374
+ x1, y1, x2, y2 = rect.coords
375
+ y1 = y1 - @@drawing.options[:boxIncrement]
376
+ rect.coords = [x1, y1, x2, y2]
377
+ if y1 < @@drawing.options[:rescaleTrigger]
378
+ base = @@drawing.base
379
+ @@drawing.bins.each do |rect|
380
+ x1, y1, x2, y2 = rect.coords
381
+ y1 = base - ((base - y1) / 2)
382
+ rect.coords = [x1, y1, x2, y2]
383
+ end
384
+ @@drawing.options[:boxIncrement] /= 2
464
385
  end
386
+ return true
387
+ end
465
388
 
466
- def add_point(i,j)
467
- x = (i.to_f / @max) * @@canvasWidth
468
- y = (j.to_f / @max) * @@canvasHeight
469
- @points << TkcRectangle.new( @canvas, x, y, x+@@dotSize, y+@@dotSize, :outline => @@dotColor, :fill => @@dotColor )
470
- # @points.last(@@traceSize).each_with_index { |p,i| p[:outline] = p['fill'] = @palette[i] }
471
- end
472
-
473
- def erase_objects
474
- zap(@ticks)
475
- zap(@bins)
476
- zap(@points)
477
- @title.delete if @title
478
- @line.delete if @line
479
- return nil
480
- end
481
-
482
- private
483
-
484
- def zap(a)
485
- a.each { |x| x.delete }
486
- a.clear
389
+ def get_counts
390
+ if @@drawing.class == Histogram
391
+ return @@drawing.counts
392
+ else
393
+ puts "current drawing is not a histogram"
394
+ return nil
487
395
  end
488
-
489
- # Make a range of colors starting from first and going to last in n steps.
490
- # First and last are expected to be 3-tuples of integer RGB values. The
491
- # result is an array that starts with first, has n-1 intermediate colors,
492
- # and ends with last. Example:
493
- # makePalette( [255,0,0], [0,0,0], 10)
494
- # makes 11 colors starting with red and ending with black.
495
-
496
- def makePalette(first, last, n)
497
- d = Array.new(3)
498
- 3.times { |i| d[i] = (first[i] - last[i]) / n }
499
- a = [first]
500
- (n-1).times do |i|
501
- a << a.last.clone
502
- 3.times { |j| a.last[j] -= d[j] }
503
- end
504
- a << last
505
- a.map { |c| sprintf("#%02X%02X%02X",c[0],c[1],c[2]) }
506
- end
507
-
508
- def initialize(parent)
509
- content = TkFrame.new(parent)
510
- @canvas = TkCanvas.new(content, :borderwidth => 1, :width => @@canvasWidth, :height => @@canvasHeight)
511
- @canvas.grid :column => 0, :row => 0, :columnspan => 4, :padx => 10, :pady => 10
512
- content.pack :pady => 20
513
- @palette = makePalette( [128,128,255], [32, 32, 255], @@traceSize-1 )
514
- @palette << "#0000FF"
515
- @ticks = Array.new
516
- @bins = Array.new
517
- @points = Array.new
518
- end
519
-
520
- end # class Draw
521
-
396
+ end
397
+
398
+ def view_dotplot(npoints, userOptions = {})
399
+ Canvas.init(500, 500, "RandomLab::DotPlot")
400
+ options = @@dotPlotOptions.merge(userOptions)
401
+ @@drawing = DotPlot.new(npoints, options)
402
+ return true
403
+ end
404
+
405
+ def plot_point(x,y)
406
+ if @@drawing.class != DotPlot
407
+ puts "call view_dotplot to initialize a dot plot"
408
+ elsif x < 0 || x >= @@drawing.max || y < 0 || y >= @@drawing.max
409
+ puts "plot_point: 0 <= x, y < #{@@drawing.max}"
410
+ else
411
+ px = (x.to_f / @@drawing.max) * Canvas.width
412
+ py = (y.to_f / @@drawing.max) * Canvas.height
413
+ r = @@drawing.options[:dotRadius]
414
+ color = @@drawing.options[:dotColor]
415
+ Canvas.circle( px, py, r, :outline => color, :fill => color )
416
+ end
417
+ return nil
418
+ end
419
+
420
+ NumberLine = Struct.new(:line, :npoints, :options)
421
+ Histogram = Struct.new(:bins, :max, :keys, :counts, :base, :options)
422
+ DotPlot = Struct.new(:max, :options)
423
+
424
+ @@numberLineOptions = {
425
+ :lineThickness => 3,
426
+ :lineColor => '#777777',
427
+ :tickHeight => 20,
428
+ :tickWidth => 1,
429
+ :tickColor => '#0000FF',
430
+ }
431
+
432
+ @@histogramOptions = {
433
+ :binColor => '#000080',
434
+ :boxIncrement => 8.0,
435
+ :rescaleTrigger => 50,
436
+ }
437
+
438
+ @@dotPlotOptions = {
439
+ :dotColor => '#000080',
440
+ :dotRadius => 1.0,
441
+ }
442
+
522
443
  @@drawing = nil
523
- @@views = [:line, :dotplot, :histogram]
524
- @@delay = 0.01
525
- @@keys = nil
444
+ @@delay = 0.1
526
445
 
527
446
  end # RandomLab
528
447