rubylabs 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rubylabs.rb CHANGED
@@ -494,38 +494,212 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
494
494
 
495
495
  module Canvas
496
496
 
497
+ class TkObject
498
+
499
+ attr_accessor :name, :coords, :penpoint
500
+
501
+ def TkObject.reset(p)
502
+ @@pipe = p
503
+ @@id = 0
504
+ end
505
+
506
+ def TkObject.nextId
507
+ @@id += 1
508
+ return "obj" + @@id.to_s
509
+ end
510
+
511
+ def TkObject.options(h)
512
+ a = []
513
+ h.each do |k,v|
514
+ a << "-#{k} #{v}"
515
+ end
516
+ return a.join(" ")
517
+ end
518
+
519
+ def raise
520
+ @@pipe.puts ".canvas raise $#{@name}"
521
+ end
522
+
523
+ def lower
524
+ @@pipe.puts ".canvas lower $#{@name}"
525
+ end
526
+
527
+ def erase
528
+ @@pipe.puts ".canvas delete $#{@name}"
529
+ end
530
+
531
+ def coords=(a)
532
+ @coords = a
533
+ @@pipe.puts ".canvas coords $#{@name} #{a.join(' ')}"
534
+ end
535
+
536
+ def fill=(x)
537
+ @@pipe.puts ".canvas itemconfigure $#{name} -fill #{x}"
538
+ end
539
+
540
+ end
541
+
542
+ =begin rdoc
543
+ Draw a line from (x0,y0) to (x1,y1)
544
+ =end
545
+
546
+ class Line < TkObject
547
+ def initialize(x0, y0, x1, y1, args = {})
548
+ raise "No canvas" unless @@pipe
549
+ @name = TkObject.nextId
550
+ @coords = [ x0, y0, x1, y1 ]
551
+ @penpoint = nil
552
+ cmnd = "set #{@name} [.canvas create line #{@coords.join(" ")} #{TkObject.options(args)}]"
553
+ @@pipe.puts cmnd
554
+ end
555
+
556
+ def Line.erase_all(tag)
557
+ @@pipe.puts ".canvas delete withtag #{tag}"
558
+ end
559
+
560
+ end
561
+
562
+ =begin rdoc
563
+ Create a circle with center at (x,y) and radius r.
564
+ =end
565
+
566
+ class Circle < TkObject
567
+ def initialize(x, y, r, args = {})
568
+ raise "No canvas" unless @@pipe
569
+ @name = TkObject.nextId
570
+ @coords = [ x-r, y-r, x+r, y+r ]
571
+ @penpoint = [ r, r ]
572
+ cmnd = "set #{@name} [.canvas create oval #{@coords.join(" ")} #{TkObject.options(args)}]"
573
+ @@pipe.puts cmnd
574
+ end
575
+ end
576
+
577
+ =begin rdoc
578
+ Create a rectangle with upper left at (x0,y0) and lower right at (x1,y1).
579
+ =end
580
+
581
+ class Rectangle < TkObject
582
+ def initialize(x0, y0, x1, y1, args = {})
583
+ raise "No canvas" unless @@pipe
584
+ @name = TkObject.nextId
585
+ @coords = [ x0, y0, x1, y1 ]
586
+ @penpoint = [ (x1-x0)/2, (y1-y0)/2 ]
587
+ cmnd = "set #{@name} [.canvas create rectangle #{@coords.join(" ")} #{TkObject.options(args)}]"
588
+ @@pipe.puts cmnd
589
+ end
590
+ end
591
+
592
+ =begin rdoc
593
+ Create a polygon with vertices defined by array a. The array can be a flat
594
+ list of x's and y's (e.g. [100,100,100,200,200,100]) or a list of (x,y)
595
+ pairs (e.g. [[100,100], [100,200], [200,100]]).
596
+ =end
597
+
598
+ class Polygon < TkObject
599
+ def initialize(a, args = {})
600
+ raise "No canvas" unless @@pipe
601
+ @name = TkObject.nextId
602
+ @coords = a
603
+ @penpoint = [0,0]
604
+ cmnd = "set #{@name} [.canvas create polygon #{@coords.join(" ")} #{TkObject.options(args)}]"
605
+ @@pipe.puts cmnd
606
+ end
607
+ end
608
+
609
+ =begin rdoc
610
+ Add text at (x, y). Sets the anchor point to northwest unless it is passed
611
+ as an argument.
612
+ =end
613
+
614
+ class Text < TkObject
615
+ def initialize(s, x, y, args = {})
616
+ raise "No canvas" unless @@pipe
617
+ @name = TkObject.nextId
618
+ @coords = [ x, y ]
619
+ @penpoint = nil
620
+ args[:anchor] = :nw unless args.has_key?(:anchor)
621
+ args[:text] = "\"#{s}\""
622
+ cmnd = "set #{@name} [.canvas create text #{@coords.join(" ")} #{TkObject.options(args)}]"
623
+ @@pipe.puts cmnd
624
+ end
625
+
626
+ def update(s)
627
+ @@pipe.puts ".canvas itemconfigure $#{name} -text \"#{s}\""
628
+ end
629
+ end
630
+
631
+ class Font < TkObject
632
+
633
+ @@fonts = []
634
+
635
+ def initialize(name, args)
636
+ @name = name
637
+ if @@fonts.index(name).nil?
638
+ cmnd = "font create #{name} #{TkObject.options(args)}"
639
+ @@pipe.puts cmnd
640
+ @@fonts << name
641
+ end
642
+ end
643
+
644
+ end
645
+
646
+ =begin rdoc
647
+ Initialize a drawing canvas. This version uses Tk to draw objects used during
648
+ a visualization. If it's not already initialized, open a connection to a wish
649
+ shell and configure the window.
650
+ =end
651
+
652
+ =begin
653
+ TODO Read the path to wish from a configuration file, so users can alter it
654
+ TODO there are probably other configuration options that can go there, too
655
+ =end
656
+
657
+ @@tkpipe = nil
658
+ @@title = ""
659
+ @@height = 0
660
+ @@width = 0
661
+
497
662
  def Canvas.init(width, height, title, *opts)
498
- require 'tk'
499
663
 
500
664
  @@title = "RubyLabs::#{title}"
501
665
  @@width = width
502
666
  @@height = height
667
+ pad = 12
503
668
 
504
- if @@tkroot.nil?
505
- # @@tkroot = TkRoot.new { title @@title }
506
- # @@content = TkFrame.new(@@tkroot)
507
- # @@canvas = TkCanvas.new(@@content, :width => width, :height => height)
508
- # @@canvas.grid :column => 0, :row => 0, :columnspan => 4, :padx => 10, :pady => 10
509
- # @@content.pack :pady => 20
510
-
511
- @@tkroot = TkRoot.new { title @@title }
512
- # @@content = TkFrame.new(@@tkroot)
513
- @@canvas = TkCanvas.new(@@tkroot, :width => width, :height => height)
514
- # @@canvas.grid :column => 0, :row => 0, :columnspan => 4, :padx => 10, :pady => 10
515
- @@canvas.pack :padx => 10, :pady => 10
516
- # @@content.pack :pady => 20
517
-
518
- @@threads = []
519
- @@threads << Thread.new() do
520
- Tk.mainloop
521
- end
522
- else
523
- Canvas.clear
524
- @@canvas.height = height
525
- @@canvas.width = width
526
- @@tkroot.title = @@title
527
- end
528
- return opts[0] == :debug ? @@canvas : true
669
+ if @@tkpipe.nil?
670
+ if Canvas.OS == "Windows"
671
+ @@tkpipe = IO.popen("wish", "w")
672
+ elsif Canvas.OS == "Linux"
673
+ @@tkpipe = IO.popen("/opt/ActiveTcl-8.5/bin/wish", "w")
674
+ else
675
+ @@tkpipe = IO.popen("/usr/local/bin/wish", "w")
676
+ end
677
+ at_exit { Canvas.close }
678
+ @@tkpipe.puts "tk::canvas .canvas -bg white -width #{width} -height #{height}"
679
+ @@tkpipe.puts ". configure -bg gray"
680
+ @@tkpipe.puts "pack .canvas -padx #{pad} -pady #{pad}"
681
+ TkObject.reset(@@tkpipe)
682
+ else
683
+ @@tkpipe.puts ".canvas delete all"
684
+ @@tkpipe.puts ".canvas configure -width #{width} -height #{height}"
685
+ end
686
+
687
+ @@tkpipe.puts "wm geometry . #{width+2*pad}x#{height+2*pad}+30+50"
688
+ @@tkpipe.puts "wm title . #{@@title}"
689
+
690
+ return opts[0] == :debug ? @@tkpipe : true
691
+ end
692
+
693
+ def Canvas.close
694
+ if @@tkpipe
695
+ @@tkpipe.puts "exit"
696
+ @@tkpipe = nil
697
+ TkObject.reset(nil)
698
+ end
699
+ end
700
+
701
+ def Canvas.flush
702
+ @@tkpipe.flush
529
703
  end
530
704
 
531
705
  def Canvas.width
@@ -536,14 +710,10 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
536
710
  @@height
537
711
  end
538
712
 
539
- def Canvas.root
540
- @@tkroot
713
+ def Canvas.pipe
714
+ @@tkpipe
541
715
  end
542
-
543
- def Canvas.create(*args)
544
- @@canvas.send(:create, *args)
545
- end
546
-
716
+
547
717
  # Idea for the future, abandoned for now: allow applications to define a coordinate
548
718
  # system, e.g. cartesian with the origin in the middle of the canvas, and map coords
549
719
  # pass to line, rectangle, etc from user-specified coordinates to Tk coordinates.
@@ -567,82 +737,21 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
567
737
  # a[i], a[i+1] = @@map.call( a[i], a[i+1] )
568
738
  # end
569
739
  # end
570
-
571
- def Canvas.clear
572
- @@objects.each { |x| x.delete }
573
- @@objects.clear
574
- end
575
-
576
- # total hack -- if on OS X and version is 1.8 and called directly from irb pause to
577
- # synch the drawing thread....
578
-
579
- def Canvas.sync
580
- if RUBY_VERSION =~ %r{^1\.8} && RUBY_PLATFORM =~ %r{darwin} && caller[1].index("(irb)") == 0
581
- sleep(0.1)
582
- end
583
- # @@tkroot.update :idletasks
584
- end
585
-
586
- =begin rdoc
587
- Add text at (x, y). Note -- looks like :anchor is required, otherwise runtime error
588
- from Tk (something about illegal coords).
589
- =end
590
-
591
- def Canvas.text(s, x, y, opts = {})
592
- return nil unless @@canvas
593
- opts[:anchor] = :nw unless opts.has_key?(:anchor)
594
- opts[:text] = s
595
- text = TkcText.new( @@canvas, x, y, opts)
596
- @@objects << text
597
- return text
598
- end
599
-
600
- def Canvas.font(options)
601
- return TkFont.new(options)
602
- end
603
-
604
- =begin rdoc
605
- Draw a line from (x0,y0) to (x1,y1)
606
- =end
607
-
608
- def Canvas.line(x0, y0, x1, y1, opts = {})
609
- return nil unless @@canvas
610
- line = TkcLine.new( @@canvas, x0, y0, x1, y1, opts )
611
- @@objects << line
612
- return line
613
- end
614
-
615
- =begin rdoc
616
- Draw a rectangle with upper left at (x0,y0) and lower right at (x1,y1).
617
- =end
618
740
 
619
- def Canvas.rectangle(x0, y0, x1, y1, opts = {})
620
- return nil unless @@canvas
621
- rect = TkcRectangle.new( @@canvas, x0, y0, x1, y1, opts)
622
- Canvas.makeObject(rect, (x1-x0)/2, (y1-y0)/2)
623
- end
624
-
625
- =begin rdoc
626
- Draw a circle with center at (x,y) and radius r
627
- =end
628
-
629
- def Canvas.circle(x, y, r, opts = {})
630
- return nil unless @@canvas
631
- ulx, uly, lrx, lry = x-r, y-r, x+r, y+r
632
- circ = TkcOval.new( @@canvas, ulx, uly, lrx, lry, opts )
633
- Canvas.makeObject(circ, r, r)
634
- end
635
-
636
- =begin rdoc
637
- Draw a polygon with vertices defined by array a. The array can be a flat
638
- list of x's and y's (e.g. [100,100,100,200,200,100]) or a list of (x,y)
639
- pairs (e.g. [[100,100], [100,200], [200,100]]).
640
- =end
641
-
642
- def Canvas.polygon(a, opts = {})
643
- return nil unless @@canvas
644
- poly = TkcPolygon.new( @@canvas, a, opts)
645
- Canvas.makeObject(poly, 0, 0)
741
+ # Figure out what type of system we're on.
742
+ # TODO: find out what the platform string is for the legacy windows one-click installer;
743
+ # the new installer uses the "mingw" compiler.
744
+
745
+ def Canvas.OS
746
+ if RUBY_PLATFORM =~ %r{darwin}
747
+ return "OS X"
748
+ elsif RUBY_PLATFORM =~ %r{linux}
749
+ return "Linux"
750
+ elsif RUBY_PLATFORM =~ %r{mingw}
751
+ return "Windows"
752
+ else
753
+ return "Unknown"
754
+ end
646
755
  end
647
756
 
648
757
  =begin rdoc
@@ -652,8 +761,8 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
652
761
  def Canvas.move(obj, dx, dy, option = nil)
653
762
  a = obj.coords
654
763
  if option == :track
655
- x0 = a[0] + obj.penx
656
- y0 = a[1] + obj.peny
764
+ x0 = a[0] + obj.penpoint[0]
765
+ y0 = a[1] + obj.penpoint[1]
657
766
  end
658
767
  (0...a.length).step(2) do |i|
659
768
  a[i] += dx
@@ -661,9 +770,9 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
661
770
  end
662
771
  obj.coords = a
663
772
  if option == :track
664
- x1 = a[0] + obj.penx
665
- y1 = a[1] + obj.peny
666
- @@objects << TkcLine.new( @@canvas, x0, y0, x1, y1, :width => 1, :fill => '#777777' )
773
+ x1 = a[0] + obj.penpoint[0]
774
+ y1 = a[1] + obj.penpoint[1]
775
+ Canvas::Line.new( x0, y0, x1, y1, :width => 1, :fill => '#777777' )
667
776
  obj.raise
668
777
  end
669
778
  return a
@@ -674,15 +783,15 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
674
783
  and save the object in a local list so it can be erased by Canvas.clear
675
784
  =end
676
785
 
677
- def Canvas.makeObject(obj, xoff, yoff)
678
- class <<obj
679
- attr_accessor :penx, :peny
680
- end
681
- obj.penx = xoff
682
- obj.peny = yoff
683
- @@objects << obj
684
- return obj
685
- end
786
+ # def Canvas.makeObject(obj, xoff, yoff)
787
+ # class <<obj
788
+ # attr_accessor :penx, :peny
789
+ # end
790
+ # obj.penx = xoff
791
+ # obj.peny = yoff
792
+ # @@objects << obj
793
+ # return obj
794
+ # end
686
795
 
687
796
  =begin rdoc
688
797
  Rotate an object by an angle theta (expressed in degrees). The object is
@@ -711,16 +820,14 @@ Call +a.random(:success)+ to get a value that is in the array +a+, or call
711
820
  def Canvas.degrees(rad)
712
821
  180 * rad / Math::PI
713
822
  end
714
-
715
- @@tkroot = nil
716
- @@objects = Array.new
717
- @@title = ""
718
- @@height = 0
719
- @@width = 0
720
- @@map = lambda { |x,y| return x, y }
721
-
823
+
722
824
  =begin rdoc
723
- Make an array of n colors that vary from first to last.
825
+ # Make a range of colors starting from first and going to last in n steps.
826
+ # First and last are expected to be 3-tuples of integer RGB values. The
827
+ # result is an array that starts with first, has n-1 intermediate colors,
828
+ # and ends with last. Example:
829
+ # makePalette( [255,0,0], [0,0,0], 10)
830
+ # makes 11 colors starting with red and ending with black.
724
831
  =end
725
832
 
726
833
  def Canvas.palette(first, last, n)
data/lib/spherelab.rb CHANGED
@@ -215,7 +215,7 @@ module SphereLab
215
215
  copy.position = position.clone
216
216
  copy.velocity = velocity.clone
217
217
  copy.force = force.clone
218
- copy.graphic = Canvas.circle(prevx, prevy, size, :fill => color)
218
+ copy.graphic = Canvas::Circle.new(prevx, prevy, size, :fill => color)
219
219
  end
220
220
  return copy
221
221
  end
@@ -327,7 +327,6 @@ module SphereLab
327
327
  @velocity.y = y
328
328
  if @graphic
329
329
  Canvas.rotate(@graphic,alpha)
330
- Canvas.sync
331
330
  end
332
331
  return alpha
333
332
  end
@@ -338,7 +337,6 @@ module SphereLab
338
337
  @position.add( @velocity * dt )
339
338
  if @graphic
340
339
  Canvas.move(@graphic, @position.x-prevx, prevy-@position.y, @tracking)
341
- Canvas.sync
342
340
  end
343
341
  return dt
344
342
  end
@@ -354,7 +352,6 @@ module SphereLab
354
352
  theta = Canvas.degrees(mid.angle(@velocity))
355
353
  alpha = 2 * (90.0 - theta)
356
354
  turn(alpha)
357
- Canvas.sync if @graphic
358
355
  return alpha
359
356
  end
360
357
 
@@ -386,9 +383,9 @@ module SphereLab
386
383
  poly[i] += edge/10
387
384
  poly[i+1] += edge/2
388
385
  end
389
- Canvas.init(edge, edge, "SphereLab")
386
+ Canvas.init(edge, edge, "SphereLab::Robot")
390
387
  turtle = Turtle.new( :x => edge/10, :y => edge/2, :heading => 0, :speed => 10 )
391
- turtle.graphic = Canvas.polygon(poly, :outline => 'black', :fill => '#00ff88')
388
+ turtle.graphic = Canvas::Polygon.new(poly, :outline => 'black', :fill => '#00ff88')
392
389
  if options[:flag]
393
390
  turtle.set_flag( *options[:flag] )
394
391
  end
@@ -398,17 +395,15 @@ module SphereLab
398
395
  class <<turtle
399
396
  def plant_flag
400
397
  set_flag( @position.x, @position.y )
401
- Canvas.sync
402
398
  end
403
399
  end
404
400
  @@drawing = TurtleView.new( turtle, options )
405
- Canvas.sync
406
401
  return true
407
402
  end
408
403
 
409
404
  def set_flag(fx, fy)
410
405
  r = 3.0
411
- Canvas.circle( fx + r/2, fy + r/2, r, :fill => 'darkblue' )
406
+ Canvas::Circle.new( fx + r/2, fy + r/2, r, :fill => 'darkblue' )
412
407
  @reference = [ fx, fy ]
413
408
  end
414
409
 
@@ -574,7 +569,7 @@ module SphereLab
574
569
  =end
575
570
 
576
571
  def view_system(blist, userOptions = {})
577
- Canvas.init(700, 700, "SphereLab")
572
+ Canvas.init(700, 700, "SphereLab::N-Body")
578
573
  if prev = @@drawing
579
574
  prev.bodies.each { |x| x.graphic = nil }
580
575
  end
@@ -583,7 +578,7 @@ module SphereLab
583
578
  sf = setScale(blist, options[:origin], options[:scale])
584
579
  blist.each do |b|
585
580
  x, y = scale(b.position, origin, sf)
586
- b.graphic = Canvas.circle(x, y, b.size, :fill => b.color)
581
+ b.graphic = Canvas::Circle.new(x, y, b.size, :fill => b.color)
587
582
  b.prevx = x
588
583
  b.prevy = y
589
584
  end
@@ -594,7 +589,6 @@ module SphereLab
594
589
  elsif options.has_key?(:pendown)
595
590
  options[:pendown] = :track
596
591
  end
597
- Canvas.sync
598
592
  return true
599
593
  end
600
594
 
@@ -622,7 +616,6 @@ module SphereLab
622
616
  falling.prevx = newx
623
617
  falling.prevy = newy
624
618
  falling.clear_force
625
- Canvas.sync
626
619
  return true
627
620
  end
628
621
 
@@ -668,7 +661,6 @@ module SphereLab
668
661
  b.prevx = newx
669
662
  b.prevy = newy
670
663
  end
671
- Canvas.sync
672
664
  return true
673
665
  end
674
666
 
@@ -728,15 +720,14 @@ module SphereLab
728
720
  mxmin = options[:mxmin]
729
721
  mymin = options[:mymin]
730
722
  hmax = options[:hmax]
731
- Canvas.init(edge, edge, "SphereLab")
732
- earth = Canvas.circle(200, 2150, 1800, :fill => blist[1].color)
723
+ Canvas.init(edge, edge, "SphereLab::Melon")
724
+ earth = Canvas::Circle.new(200, 2150, 1800, :fill => blist[1].color)
733
725
  blist[1].graphic = earth
734
726
  mymax = earth.coords[1]
735
- melon = Canvas.circle(mxmin, mymax, 5, :fill => blist[0].color)
727
+ melon = Canvas::Circle.new(mxmin, mymax, 5, :fill => blist[0].color)
736
728
  blist[0].graphic = melon
737
729
  scale = (mymax-mymin) / hmax.to_f
738
730
  @@drawing = MelonView.new(blist, scale, blist[0].position.y, melon.coords, options)
739
- Canvas.sync
740
731
  return true
741
732
  end
742
733
 
@@ -759,7 +750,6 @@ module SphereLab
759
750
  a[1] -= height * @@drawing.scale
760
751
  a[3] -= height * @@drawing.scale
761
752
  melon.graphic.coords = a
762
- Canvas.sync
763
753
  else
764
754
  melon.prevy = melon.position.y
765
755
  melon.position.y += height
@@ -784,7 +774,6 @@ module SphereLab
784
774
  dx = (melon.position.x - mx) * @@drawing.scale
785
775
  dy = (my - melon.position.y) * @@drawing.scale
786
776
  Canvas.move(melon.graphic, dx, dy, @@drawing.options[:pendown])
787
- Canvas.sync
788
777
  end
789
778
  return blist[0].height
790
779
  end
@@ -797,7 +786,6 @@ module SphereLab
797
786
  break if res == "splat"
798
787
  count += 1
799
788
  end
800
- Canvas.sync if @@drawing
801
789
  return count * dt
802
790
  end
803
791
  # :end :drop_melon