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/lib/rubylabs.rb CHANGED
@@ -24,8 +24,6 @@ autoload :ElizaLab, "elizalab.rb"
24
24
  autoload :SphereLab, "spherelab.rb"
25
25
  autoload :TSPLab, "tsplab.rb"
26
26
 
27
- autoload :Viewer, "viewer.rb"
28
-
29
27
  module RubyLabs
30
28
 
31
29
  =begin rdoc
@@ -432,52 +430,278 @@ Similar to TestArray, but draws random words from a file.
432
430
  to make sure line numbers are valid.
433
431
  =end
434
432
 
435
- def Source.lines(spec, id)
436
- if spec.class == Fixnum && Source.range_check(spec, id)
437
- return [spec]
438
- elsif spec.class == Array
439
- res = Array.new
440
- spec.each do |line|
441
- raise "line number must be an integer" unless line.class == Fixnum
442
- res << line if Source.range_check(line, id)
443
- end
444
- return res
445
- elsif spec.class == String
446
- res = Array.new
447
- for i in @@base[id]..(@@base[id]+@@size[id]-1)
448
- line = SCRIPT_LINES__[@@file[id]][i-1].chomp
449
- res << i - @@base[id] + 1 if line.index(spec)
450
- end
451
- return res
452
- else
453
- raise "invalid spec: '#{spec}' (must be an integer, array of integers, or a pattern)"
454
- end
455
- end
433
+ def Source.lines(spec, id)
434
+ if spec.class == Fixnum && Source.range_check(spec, id)
435
+ return [spec]
436
+ elsif spec.class == Array
437
+ res = Array.new
438
+ spec.each do |line|
439
+ raise "line number must be an integer" unless line.class == Fixnum
440
+ res << line if Source.range_check(line, id)
441
+ end
442
+ return res
443
+ elsif spec.class == String
444
+ res = Array.new
445
+ for i in @@base[id]..(@@base[id]+@@size[id]-1)
446
+ line = SCRIPT_LINES__[@@file[id]][i-1].chomp
447
+ res << i - @@base[id] + 1 if line.index(spec)
448
+ end
449
+ return res
450
+ else
451
+ raise "invalid spec: '#{spec}' (must be an integer, array of integers, or a pattern)"
452
+ end
453
+ end
456
454
 
457
- def Source.range_check(n, id)
458
- max = @@size[id]
459
- raise "line number must be between 1 and #{max}" unless n >= 1 && n <= max
460
- return true
461
- end
455
+ def Source.range_check(n, id)
456
+ max = @@size[id]
457
+ raise "line number must be between 1 and #{max}" unless n >= 1 && n <= max
458
+ return true
459
+ end
462
460
 
463
461
  =begin rdoc
464
462
  Internal use only -- show info about method
465
463
  =end
466
464
 
467
- def Source.info(name)
468
- unless id = Source.find(name)
469
- puts "Can't find method named '#{name}'"
470
- return
471
- end
465
+ def Source.info(name)
466
+ unless id = Source.find(name)
467
+ puts "Can't find method named '#{name}'"
468
+ return
469
+ end
472
470
 
473
- printf "file: %s\n", @@file[name]
474
- printf "size: %d\n", @@size[name]
475
- printf "base: %d\n", @@base[name]
476
- printf "probes: %s\n", @@probes[name].inspect
477
- end
471
+ printf "file: %s\n", @@file[name]
472
+ printf "size: %d\n", @@size[name]
473
+ printf "base: %d\n", @@base[name]
474
+ printf "probes: %s\n", @@probes[name].inspect
475
+ end
478
476
 
479
477
  end # Source
480
478
 
479
+ =begin rdoc
480
+ Module for interactive graphics.
481
+ =end
482
+
483
+ module Canvas
484
+
485
+ def Canvas.init(width, height, title, *opts)
486
+ require 'tk'
487
+
488
+ @@title = "RubyLabs::#{title}"
489
+ @@width = width
490
+ @@height = height
491
+
492
+ if @@tkroot.nil?
493
+ # @@tkroot = TkRoot.new { title @@title }
494
+ # @@content = TkFrame.new(@@tkroot)
495
+ # @@canvas = TkCanvas.new(@@content, :width => width, :height => height)
496
+ # @@canvas.grid :column => 0, :row => 0, :columnspan => 4, :padx => 10, :pady => 10
497
+ # @@content.pack :pady => 20
498
+
499
+ @@tkroot = TkRoot.new { title @@title }
500
+ # @@content = TkFrame.new(@@tkroot)
501
+ @@canvas = TkCanvas.new(@@tkroot, :width => width, :height => height)
502
+ # @@canvas.grid :column => 0, :row => 0, :columnspan => 4, :padx => 10, :pady => 10
503
+ @@canvas.pack :padx => 10, :pady => 10
504
+ # @@content.pack :pady => 20
505
+
506
+ @@threads = []
507
+ @@threads << Thread.new() do
508
+ Tk.mainloop
509
+ end
510
+ else
511
+ Canvas.clear
512
+ @@canvas.height = height
513
+ @@canvas.width = width
514
+ @@tkroot.title = @@title
515
+ end
516
+ return opts[0] == :debug ? @@canvas : true
517
+ end
518
+
519
+ def Canvas.width
520
+ @@width
521
+ end
522
+
523
+ def Canvas.height
524
+ @@height
525
+ end
526
+
527
+ def Canvas.root
528
+ @@tkroot
529
+ end
530
+
531
+ # Idea for the future, abandoned for now: allow applications to define a coordinate
532
+ # system, e.g. cartesian with the origin in the middle of the canvas, and map coords
533
+ # pass to line, rectangle, etc from user-specified coordinates to Tk coordinates.
534
+ # Lots of complications, though -- e.g. if an application calls the coords method to
535
+ # get object coordinates (e.g. RandomLab#add_tick gets y coordinate of number line)
536
+ # it will have to map back from Tk to the selected mapping.....
537
+
538
+ # def Canvas.origin(option)
539
+ # case option
540
+ # when :tk
541
+ # @@map = lambda { |x,y| return x, y }
542
+ # when :quadrant
543
+ # @@map = lambda { |x,y| return x, @@height - y }
544
+ # when :cartesian
545
+ # @@map = lambda { |x,y| return x + @@width/2, @@height/2 - y }
546
+ # end
547
+ # end
548
+ #
549
+ # def Canvas.map(a)
550
+ # (0...a.length).step(2) do |i|
551
+ # a[i], a[i+1] = @@map.call( a[i], a[i+1] )
552
+ # end
553
+ # end
554
+
555
+ def Canvas.clear
556
+ @@objects.each { |x| x.delete }
557
+ @@objects.clear
558
+ end
559
+
560
+ # total hack -- if on OS X and version is 1.8 and called directly from irb pause to
561
+ # synch the drawing thread....
562
+
563
+ def Canvas.sync
564
+ if RUBY_VERSION =~ %r{^1\.8} && RUBY_PLATFORM =~ %r{darwin} && caller[2] =~ %r{workspace}
565
+ sleep(0.1)
566
+ end
567
+ end
568
+
569
+ =begin rdoc
570
+ Draw a line from (x0,y0) to (x1,y1)
571
+ =end
572
+
573
+ def Canvas.line(x0, y0, x1, y1, opts = {})
574
+ return nil unless @@canvas
575
+ line = TkcLine.new( @@canvas, x0, y0, x1, y1, opts )
576
+ @@objects << line
577
+ return line
578
+ end
579
+
580
+ =begin rdoc
581
+ Draw a rectangle with upper left at (x0,y0) and lower right at (x1,y1).
582
+ =end
583
+
584
+ def Canvas.rectangle(x0, y0, x1, y1, opts = {})
585
+ return nil unless @@canvas
586
+ rect = TkcRectangle.new( @@canvas, x0, y0, x1, y1, opts)
587
+ Canvas.makeObject(rect, (x1-x0)/2, (y1-y0)/2)
588
+ end
589
+
590
+ =begin rdoc
591
+ Draw a circle with center at (x,y) and radius r
592
+ =end
593
+
594
+ def Canvas.circle(x, y, r, opts = {})
595
+ return nil unless @@canvas
596
+ ulx, uly, lrx, lry = x-r, y-r, x+r, y+r
597
+ circ = TkcOval.new( @@canvas, ulx, uly, lrx, lry, opts )
598
+ Canvas.makeObject(circ, r, r)
599
+ end
600
+
601
+ =begin rdoc
602
+ Draw a polygon with vertices defined by array a. The array can be a flat
603
+ list of x's and y's (e.g. [100,100,100,200,200,100]) or a list of (x,y)
604
+ pairs (e.g. [[100,100], [100,200], [200,100]]).
605
+ =end
606
+
607
+ def Canvas.polygon(a, opts = {})
608
+ return nil unless @@canvas
609
+ poly = TkcPolygon.new( @@canvas, a, opts)
610
+ Canvas.makeObject(poly, 0, 0)
611
+ end
612
+
613
+ =begin rdoc
614
+ Move an object by an amount dx, dy
615
+ =end
616
+
617
+ def Canvas.move(obj, dx, dy, option = nil)
618
+ a = obj.coords
619
+ if option == :track
620
+ x0 = a[0] + obj.penx
621
+ y0 = a[1] + obj.peny
622
+ end
623
+ (0...a.length).step(2) do |i|
624
+ a[i] += dx
625
+ a[i+1] += dy
626
+ end
627
+ obj.coords = a
628
+ if option == :track
629
+ x1 = a[0] + obj.penx
630
+ y1 = a[1] + obj.peny
631
+ @@objects << TkcLine.new( @@canvas, x0, y0, x1, y1, :width => 1, :fill => '#777777' )
632
+ obj.raise
633
+ end
634
+ return a
635
+ end
636
+
637
+ =begin rdoc
638
+ Attach a "pen point" to an object by adding new accessor methods named penx and peny,
639
+ and save the object in a local list so it can be erased by Canvas.clear
640
+ =end
641
+
642
+ def Canvas.makeObject(obj, xoff, yoff)
643
+ class <<obj
644
+ attr_accessor :penx, :peny
645
+ end
646
+ obj.penx = xoff
647
+ obj.peny = yoff
648
+ @@objects << obj
649
+ return obj
650
+ end
651
+
652
+ =begin rdoc
653
+ Rotate an object by an angle theta (expressed in degrees). The object is
654
+ rotated about the point defined by the first pair of (x,y) coordinates.
655
+ =end
656
+
657
+ def Canvas.rotate(obj, theta)
658
+ theta = Canvas.radians(theta)
659
+ a = obj.coords
660
+ x0 = a[0]
661
+ y0 = a[1]
662
+ (0...a.length).step(2) do |i|
663
+ x = a[i] - x0
664
+ y = a[i+1] - y0
665
+ a[i] = x0 + x * Math.cos(theta) - y * Math.sin(theta)
666
+ a[i+1] = y0 + x * Math.sin(theta) + y * Math.cos(theta)
667
+ end
668
+ obj.coords = a
669
+ return a
670
+ end
671
+
672
+ def Canvas.radians(deg)
673
+ deg * Math::PI / 180
674
+ end
675
+
676
+ def Canvas.degrees(rad)
677
+ 180 * rad / Math::PI
678
+ end
679
+
680
+ @@tkroot = nil
681
+ @@objects = Array.new
682
+ @@title = ""
683
+ @@height = 0
684
+ @@width = 0
685
+ @@map = lambda { |x,y| return x, y }
686
+
687
+ =begin rdoc
688
+ Make an array of n colors that vary from first to last.
689
+ =end
690
+
691
+ def Canvas.palette(first, last, n)
692
+ d = Array.new(3)
693
+ 3.times { |i| d[i] = (first[i] - last[i]) / n }
694
+ a = [first]
695
+ (n-1).times do |i|
696
+ a << a.last.clone
697
+ 3.times { |j| a.last[j] -= d[j] }
698
+ end
699
+ a << last
700
+ a.map { |c| sprintf("#%02X%02X%02X",c[0],c[1],c[2]) }
701
+ end
702
+
703
+ end # Canvas
704
+
481
705
  =begin rdoc
482
706
  Priority queue class -- simple wrapper for an array that can only be updated via
483
707
  +<<+ and +shift+ operations. Also responds to +length+, +first+, and +last+,