rubylabs 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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