1. Brett Giles
  2. lqpl

Commits

Brett Giles  committed 5f6b79c

Refactor quantumStackPainter

Reduced flog from 205 to 63 for class, worst method from 80.3 to 15.7

New classes as a result:
- CanvasSize (may need some refactoring when we analyze it)

Monkeypatches
- Array (primarily partitioning methods)
- Point (copy point with an offset)

  • Participants
  • Parent commits 8b82970
  • Branches master

Comments (0)

Files changed (12)

File GUI/spec/painting/canvas_size_spec.rb

View file
+require 'spec/spec_helper'
+require 'src/painting/canvas_size'
+require 'src/panels/quantum_stack/quantum_stack_model'
+require 'src/panels/quantum_stack/quantum_stack_painter'
+
+describe CanvasSize do
+  describe "Class Methods" do
+    describe "node_separation" do
+      it "should have a default horizontal node sep of 55.0" do
+        CanvasSize.node_separation(:horizontal).should == 55.0
+      end
+
+      it "should have a default vertical node sep of 50.0" do
+        CanvasSize.node_separation(:vertical).should == 50.0
+      end
+    end
+    describe "total_widths" do
+      it "should give 0 for []" do
+        CanvasSize::total_widths([]).should == 0.0
+      end
+      it "should give the required size for a single element" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        CanvasSize::total_widths([c1]).should == c1.required_width
+      end
+      
+      it "should give the sume of required width for a more than one element" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        CanvasSize::total_widths([c1,c2]).should == c1.required_width + c2.required_width
+      end
+    end
+    describe "width_to_right_of_head" do
+      it "should give 0 for an empty list" do
+        CanvasSize::width_to_right_of_head([]).should == 0.0
+      end
+      it "should give right measure only for a singleton list" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        CanvasSize::width_to_right_of_head([c1]).should == 99.0
+      end 
+      it "should give right measure of head + total of 2nd for a 2 elt list" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        CanvasSize::width_to_right_of_head([c1,c2]).should == 259.0
+      end 
+      it "should give right measure  of head + total of rest for a 4 elt list" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        c3 = CanvasSize::new_with_measures(100,100,0)
+        c4 = CanvasSize::new_with_measures(200,200,0)
+        CanvasSize::width_to_right_of_head([c1,c2,c3,c4]).should == 859.0
+      end        
+    end
+    
+    describe "width_to_left_of_tail" do
+      it "should give 0 for an empty list" do
+        CanvasSize::width_to_left_of_tail([]).should == 0.0
+      end
+      it "should give left measure only for a singleton list" do
+        c1 = CanvasSize::new_with_measures(70,99,0)
+        CanvasSize::width_to_left_of_tail([c1]).should == 70.0
+      end 
+      it "should give left measure of last + total of 1st for a 2 elt list" do
+        c1 = CanvasSize::new_with_measures(50,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        CanvasSize::width_to_left_of_tail([c1,c2]).should == 224.0
+      end 
+      it "should give left measure  of last + total of rest for a 4 elt list" do
+        c1 = CanvasSize::new_with_measures(60,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        c3 = CanvasSize::new_with_measures(100,100,0)
+        c4 = CanvasSize::new_with_measures(200,200,0)
+        CanvasSize::width_to_left_of_tail([c1,c2,c3,c4]).should == 60+99+75+85+100+100+200
+      end        
+    end
+    describe "compute_offsets" do
+      it "should have a single 0 offest for a single measure" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        CanvasSize::compute_offsets([c1]).should == [0]
+      end
+      it "should be [-right,left] for a 2 elt list" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        CanvasSize::compute_offsets([c1,c2]).should == [-99,75]
+      end
+      it "should be [-(1.r+2.l), 0, (2.r+3.l)] for 3 element" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        c3 = CanvasSize::new_with_measures(100,100,0)
+        CanvasSize::compute_offsets([c1,c2,c3]).should == [-174,0,185]
+      end
+      it "should be [-(1.r+2.t), -2.r, 3.l, (3.t+4.l)] for 4 element" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        c3 = CanvasSize::new_with_measures(100,100,0)
+        c4 = CanvasSize::new_with_measures(200,200,0)
+        CanvasSize::compute_offsets([c1,c2,c3,c4]).should == [-(99+75+85),-85,100,200+200]
+      end
+      it "should be [-(1.r+2.t+3.l), -(2.r+3.l),0, (3.r), 3.t+4.l, 3.t+4.t+5.l] for 5 element" do
+        c1 = CanvasSize::new_with_measures(10,99,0)
+        c2 = CanvasSize::new_with_measures(75,85,0)
+        c3 = CanvasSize::new_with_measures(80,120,0)
+        c4 = CanvasSize::new_with_measures(200,200,0)
+        c5 = CanvasSize::new_with_measures(60,60,0)
+        CanvasSize::compute_offsets([c1,c2,c3,c4,c5]).should == [-(99+75+85+80), -(85+80),0,120+200,120+200+200+60]
+      end
+      it "should return an empty list for empty or nil input" do
+        CanvasSize::compute_offsets([]).should == []
+        CanvasSize::compute_offsets(nil).should == []
+      end
+    end
+  end
+  describe "initialization" do
+    describe "explicit measures class init" do
+      it "should accept 0 args and create an item with 0 left,right,height" do
+        c = CanvasSize.new_with_measures
+        c.left_width.should == 0.0
+        c.right_width.should == 0.0
+        c.height.should == 0.0
+      end
+      
+      it "should accept nil args and create an item with 0 left,right,height" do
+        c = CanvasSize.new_with_measures nil,nil,nil
+        c.left_width.should == 0.0
+        c.right_width.should == 0.0
+        c.height.should == 0.0
+      end
+      it "should accept 1 number arg and create an item with that as left and 0 right,height" do
+        c = CanvasSize.new_with_measures 2.0
+        c.left_width.should == 2.0
+        c.right_width.should == 0.0
+        c.height.should == 0.0
+      end
+      it "should accept 2 number args and create an item with those as left,right and zero height" do
+        c = CanvasSize.new_with_measures 2.0, 3.0
+        c.left_width.should == 2.0
+        c.right_width.should == 3.0
+        c.height.should == 0.0
+      end
+      it "should accept 3 number args and create an item with those as left,right,height" do
+        c = CanvasSize.new_with_measures 1.0, 2.0, 3.0
+        c.left_width.should == 1.0
+        c.right_width.should == 2.0
+        c.height.should == 3.0
+      end
+    end
+    it "should accept an array of ducktyped CanvasSizes, partition them and use them as left, right, height" do
+      c1 = CanvasSize.new_with_measures 100.0, 100.0, 10.0
+      c2 = CanvasSize.new_with_measures 45.0, 80.0, 20.0
+      c3 = CanvasSize.new_with_measures 70.0, 60.0, 20.0
+      c = CanvasSize.new_from_subtree [c1,c2,c3]
+      c.left_width.should == 245.0
+      c.right_width.should == 210.0
+      c.height.should == 20.0 + CanvasSize::VERTICAL_NODE_SEPARATION
+    end
+  end   
+  context "get measures" do
+    context "when sizes > mins" do
+      before (:each) do
+        @cs = CanvasSize.new_with_measures(70.0,80.0,60.0)
+      end
+      it "should give 70.0 as left_width" do
+        @cs.left_width.should == 70.0
+      end
+      it "should give 70.0 as left_required_width" do
+        @cs.left_required_width.should == 70.0
+      end
+      it "should give 150.0 as total width" do
+        @cs.total_width.should == 150.0
+      end
+      it "should give 150.0 as required width" do
+        @cs.required_width.should == 150.0
+      end
+      it "should give 80.0 as right_width" do
+        @cs.right_width.should == 80.0
+      end
+      it "should give 80.0 as right required width" do
+        @cs.right_required_width.should == 80.0
+      end
+      it "should give 60 as height" do
+        @cs.height.should == 60.0
+      end
+    end
+    
+    context "when sizes < mins" do
+      before (:each) do
+        @cs = CanvasSize.new_with_measures(20.0,20.0,20.0)
+      end
+      it "should give 20.0 as left_width" do
+        @cs.left_width.should == 20.0
+      end
+      it "should give 1/2 Node Sep as left_required_width" do
+        @cs.left_required_width.should == (CanvasSize::HORIZONTAL_NODE_SEPARATION * 0.5)
+      end
+      it "should give 40.0 as total width" do
+        @cs.total_width.should == 40.0
+      end
+      it "should give node sep as required width" do
+        @cs.required_width.should == CanvasSize::HORIZONTAL_NODE_SEPARATION
+      end
+      it "should give 20.0 as right_width" do
+        @cs.right_width.should == 20.0
+      end
+      it "should give 1/2 node sep as right required width" do
+        @cs.right_required_width.should == (CanvasSize::HORIZONTAL_NODE_SEPARATION * 0.5)
+      end
+      it "should give 20+nodesep as height with spacing" do
+        @cs.height_with_spacing.should == 20.0+CanvasSize::VERTICAL_NODE_SEPARATION
+      end
+    end
+  end
+  context "self modifies" do
+    context "max of each dimension!" do
+      before(:each) do
+        @smaller = CanvasSize.new_with_measures(20.0,20.0,20.0)
+        @bigger = CanvasSize.new_with_measures(30.0,30.0,30.0)
+      end
+      it "updates the measures if the other has bigger ones" do
+        @smaller.max_of_each_dimension!(@bigger)
+        @smaller.left_width.should == 30
+        @smaller.right_width.should == 30
+        @smaller.height.should == 30
+      end
+      it "stays the same if the other has smaller ones" do
+        l = @bigger.left_width
+        r = @bigger.right_width
+        h = @bigger.height
+        @bigger.max_of_each_dimension!(@smaller)
+        
+        @bigger.left_width.should == l
+        @bigger.right_width.should == r
+        @bigger.height.should == h
+      end
+    end
+  end
+end

File GUI/spec/utility/monkey/array_spec.rb

View file
+require 'spec/spec_helper'
+
+describe Array do
+  describe "get_left_partition" do
+    it "should get first 2 of a size four array" do
+      [1,2,3,4].get_left_partition.should == [1,2]
+    end    
+    it "should get first 2 of a size five array into 2,1,2" do
+      [1,2,3,4,5].get_left_partition.should == [1,2]
+    end
+    it "should get first 1 of a size two array" do
+      [1,2].get_left_partition.should == [1]
+    end
+    it "should get first 1 of a size three array" do
+      [1,2,3].get_left_partition.should == [1]
+    end
+    it "should get empty array from a size one array" do
+      [1].get_left_partition.should == []
+    end
+    it "should get empty array from  a size zero array" do
+      [].get_left_partition.should == []
+    end
+  end
+  
+  describe "get middle element" do
+    it "should get nil for a size 4 array" do
+      [1,2,3,4].get_middle_element.should be_nil
+    end    
+    it "should get the 3rd element of a size five array" do
+      [1,2,3,4,5].get_middle_element.should == 3
+    end
+    it "should get nil for  a size two array into 1,0,1" do
+      [1,2].get_middle_element.should be_nil
+    end
+    it "should get the second element of a size three array" do
+      [1,2,3].get_middle_element.should == 2
+    end
+    it "should get the only element of a size one array" do
+      [1].get_middle_element.should == 1
+    end
+    it "should get nil for  a size zero array" do
+      [].get_middle_element.should be_nil
+    end
+  end
+  
+  describe "get_right_partition" do
+    it "should get the last 2 of a size four array" do
+      [1,2,3,4].get_right_partition.should == [3,4]
+    end    
+    it "should get the last 2 of a size five array" do
+      [1,2,3,4,5].get_right_partition.should == [4,5]
+    end
+    it "should get the last  of a size two array" do
+      [1,2].get_right_partition.should == [2]
+    end
+    it "should get the last  of a size three array" do
+      [1,2,3].get_right_partition.should == [3]
+    end
+    it "should get empty from a size one array" do
+      [1].get_right_partition.should == []
+    end
+    it "should get empty from a size zero array" do
+      [].get_right_partition.should == []
+    end
+  end
+  
+  describe "partition into left mid right" do
+    it "should split a size four array into 2,0,2" do
+      [1,2,3,4].partion_into_left_mid_right.should == [[1,2],[],[3,4]]
+    end    
+    it "should split a size five array into 2,1,2" do
+      [1,2,3,4,5].partion_into_left_mid_right.should == [[1,2],[3],[4,5]]
+    end
+    it "should split a size two array into 1,0,1" do
+      [1,2].partion_into_left_mid_right.should == [[1],[],[2]]
+    end
+    it "should split a size three array into 1,1,1" do
+      [1,2,3].partion_into_left_mid_right.should == [[1],[2],[3]]
+    end
+    it "should split a size one array into 0,1,0" do
+      [1].partion_into_left_mid_right.should == [[],[1],[]]
+    end
+    it "should split a size zero array into 0,0,0" do
+      [].partion_into_left_mid_right.should == [[],[],[]]
+    end
+  end
+  describe "tails" do
+    it "gives [] for []" do
+      [].tails.should == []
+    end
+    
+    it "gives [[1]] for [1]" do
+      [1].tails.should == [[1]]
+    end
+    
+    it "gives [[1,2],[2]] for [1,2]" do
+      [1,2].tails.should == [[1,2],[2]]
+    end
+    
+    it "gives [[1,2,3],[2,3],[3]] for [1,2,3]" do
+      [1,2,3].tails.should ==  [[1,2,3],[2,3],[3]]
+    end
+  end
+  
+  
+  describe "heads" do
+    it "gives [] for []" do
+      [].heads.should == []
+    end
+    
+    it "gives [[1]] for [1]" do
+      [1].heads.should == [[1]]
+    end
+    
+    it "gives [[1],[1,2]] for [1,2]" do
+      [1,2].heads.should == [[1],[1,2]]
+    end
+    
+    it "gives [[1],[1,2],[1,2,3]] for [1,2,3]" do
+      [1,2,3].heads.should ==  [[1],[1,2],[1,2,3]]
+    end
+  end
+end

File GUI/spec/view/descriptor_painter_spec.rb

View file
 
   it "should have a left  width that is more than the right width" do
     g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
-    @sd.model_paint_size(g)[:left].should >  @sd.model_paint_size(g)[:right]
+    @sd.model_paint_size(g).left_width.should >  @sd.model_paint_size(g).right_width
   end
 end
 
   end
   it "should have a preferred size of W>10, H > 15" do
     g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
-    @sd.model_paint_size(g)[:left].should > 5
-    @sd.model_paint_size(g)[:right].should > 5
-    @sd.model_paint_size(g)[:height].should > 15
+    @sd.model_paint_size(g).left_width.should > 5
+    @sd.model_paint_size(g).right_width.should > 5
+    @sd.model_paint_size(g).height.should > 15
   end
 
   it "should have a left equal to the right" do
     g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
-    @sd.model_paint_size(g)[:left].should ==  @sd.model_paint_size(g)[:right]
+    @sd.model_paint_size(g).left_width.should ==  @sd.model_paint_size(g).right_width
   end
 end
 
   end
   it "should have a total size of W=16, H > 15" do
     g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
-    @sd.model_paint_size(g)[:left].should == 8
-    @sd.model_paint_size(g)[:right].should == 8
-    @sd.model_paint_size(g)[:height].should > 15
+    @sd.model_paint_size(g).left_width.should == 8
+    @sd.model_paint_size(g).right_width.should == 8
+    @sd.model_paint_size(g).height.should > 15
   end
   it "should have a left equal to 1/2 the total width" do
     g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
-    @sd.model_paint_size(g)[:left].should ==  @sd.model_paint_size(g)[:right]
+    @sd.model_paint_size(g).left_width.should ==  @sd.model_paint_size(g).total_width * 0.5
   end
 
 end

File GUI/spec/view/quantum_stack_painter_spec.rb

View file
 
 describe QuantumStackPainter do
 
-  describe "class method compute_offsets" do
-    it "should have a single 0 offest for two element lists" do
-      QuantumStackPainter::compute_offsets([30,40]).should == [0]
-    end
-    it "should be [-y,v] for the 4 elt list [_,y,v,_]" do
-      QuantumStackPainter::compute_offsets([40,30, 14,50]).should == [-30,14]
-    end
-    it "should be [-(b+c), 0, (d+e)] for the 6 element list [_,b,c,d,e,_]" do
-      QuantumStackPainter::compute_offsets([30, 14, 50,8,10,12]).should == [-64,0,18]
-    end
-    it "should ignore the first and last element, and then then accumulate the others around the midpoint" do
-      QuantumStackPainter::compute_offsets((1..10).to_a).should == [-14,-9,0,13,30]
-      QuantumStackPainter::compute_offsets((1..12).to_a).should == [-20,-15,-6,7,24,45]
-    end
-    it "should return an empty list for empty or nil input" do
-      QuantumStackPainter::compute_offsets([]).should == []
-      QuantumStackPainter::compute_offsets(nil).should == []
-    end
-  end
+
   describe "get_preferred_size_of_model" do
 
     it "should cache the size after the first call"
     it "should request the size of the descriptor on the first call when not bottom"
 
   end
+  
   describe "sizing" do
     before(:each) do
       st = double("StackTranslation")
       @g = BufferedImage.new(500,500,BufferedImage::TYPE_INT_RGB).graphics
 
     end
-    it "should have a default horizontal node sep of 55.0" do
-      @qshad.node_separation(:horizontal).should == 55.0
-    end
-
-    it "should have a default vertical node sep of 50.0" do
-      @qshad.node_separation(:vertical).should == 50.0
-    end
-    it "should have a preferred size of width > 160 and height > 60 for the hadamard qbit" do
-      ps = @qshad.model_paint_size(@g)
-      ps[:left].should > 80.0
-      ps[:right].should > 80.0
-      ps[:height].should > 60.0
-    end
-    it "should have a preferred size of width >= 25 and height >= 28 for the value of 0.5 only" do
-      ps = @qsval.model_paint_size(@g)
-      ps[:left].should > 12.5
-      ps[:right].should > 12.5
-      ps[:height].should > 28.0
-    end
-    it "should have a preferred size > 10, 15 for bottom" do
-      ps = @qsb.model_paint_size(@g)
-      ps[:left].should > 5.0
-      ps[:right].should > 5.0
-      ps[:height].should > 15.0
-    end
-    it "should have a left size bigger than right width for qsint" do
-      @qsint.model_paint_size(@g)[:left] > @qsint.model_paint_size(@g)[:right]
+    describe "bottom element size" do
+      before (:each) do
+        @bottom_size = @qsb.bottom_element_size(@g)
+      end
+      it "should have a left width of 6" do
+        @bottom_size.left_width.should == 6.0
+      end
+      it "should have a right width of 6" do
+        @bottom_size.right_width.should == 6.0
+      end
+      it "should have a height > 15 and < 16" do
+        @bottom_size.height.should > 15
+        @bottom_size.height.should < 16
+      end
     end
-    it "should have a left size ~= right for the had qubit" do
-      (@qsval.model_paint_size(@g)[:left] -  @qsval.model_paint_size(@g)[:right]).abs.should < 2
+    describe "model paint size" do
+      it "should have a preferred size of width > 160 and height > 60 for the hadamard qbit" do
+        ps = @qshad.model_paint_size(@g)
+        ps.left_width.should > 80.0
+        ps.right_width.should > 80.0
+        ps.height.should > 60.0
+      end
+      it "should have a preferred size of width >= 25 and height >= 28 for the value of 0.5 only" do
+        ps = @qsval.model_paint_size(@g)
+        ps.left_width.should > 12.5
+        ps.right_width.should > 12.5
+        ps.height.should > 28.0
+      end
+      it "should have a preferred size > 10, 15 for bottom" do
+        ps = @qsb.model_paint_size(@g)
+        ps.left_width.should > 5.0
+        ps.right_width.should > 5.0
+        ps.height.should > 15.0
+      end
+      it "should have a left size bigger than right width for qsint" do
+        @qsint.model_paint_size(@g).left_width > @qsint.model_paint_size(@g).right_width
+      end
+      it "should have a left size ~= right for the had qubit" do
+        (@qsval.model_paint_size(@g).left_width -  @qsval.model_paint_size(@g).right_width).abs.should < 2
+      end
     end
   end
 end

File GUI/src/com/drogar/lqpl/qstack/Painter.java

View file
  */
 public interface Painter {
     public void setModelElement(Object model);
-    public void paintModel(Graphics g, Component parent);
-    public void paintModelAtPoint(Graphics g, Component parent, Point center);
+    public void paintModel(Graphics g);
+    public void paintModelAtPoint(Graphics g,  Point center);
     public Icon imageOfModel();
 }

File GUI/src/manifest.rb

View file
   # Files to be added only when run from inside a jar file
 end
 
+require 'painting/canvas_size'
 require 'application_model'
+require 'utility/monkey/array'
 require 'utility/xml_decode'
 require 'xml_based_model'
 ["server_process_not_found", "invalid_input"].each do |f|

File GUI/src/painting/canvas_size.rb

View file
+class CanvasSize
+  attr_accessor :left_width
+  attr_accessor :right_width
+  attr_accessor :height
+  
+  VERTICAL_NODE_SEPARATION=50.0
+  HORIZONTAL_NODE_SEPARATION=55.0
+  def self.new_with_measures(left=0.0,right=0.0,height=0.0)
+    cs=self.new
+    cs.initialize_with_measures(left,right,height)
+    cs
+  end
+  
+  def self.new_from_subtree(subtree_array)
+    mid = subtree_array.get_middle_element
+    
+    left = CanvasSize::total_widths(subtree_array.get_left_partition)
+    left += mid.left_required_width if mid
+    
+    right = CanvasSize::total_widths(subtree_array.get_right_partition)
+    right += mid.right_required_width if mid
+    
+    h = subtree_array.collect{|cs| cs.height_with_spacing}.max
+    self.new_with_measures(left,right,h)
+  end
+  
+  def self.total_widths(sizes)
+    sizes.inject(0.0){|memo,s| memo+s.required_width}
+  end
+  
+  def self.width_to_right_of_head(sizes)
+    return 0.0 if !sizes or sizes.length == 0
+    sizes[0].right_required_width + CanvasSize::total_widths(sizes.drop(1))
+  end
+  
+  def self.width_to_left_of_tail(sizes)
+    return 0.0 if !sizes or sizes.length == 0
+    sizes.last.left_required_width + CanvasSize::total_widths(sizes.take(sizes.length-1))
+  end
+  
+  # computes offsets for painting the substacks.
+  # sum up the sizes from point to midpoint to get offsets (-ve to left, +ve to right)
+  # special cases:
+  # No sizes -> nothing to return
+  # handle having a midpoint = equals size 0
+  def self.compute_offsets(sizes)
+    return [] if !sizes or sizes.length == 0
+    mid = sizes.get_middle_element
+    lefts = sizes.get_left_partition.tails.collect {|la| -CanvasSize.width_to_right_of_head(la)}
+    lefts.collect! {|b| b-mid.left_required_width} if mid
+    lefts << 0 if mid
+    rights = sizes.get_right_partition.heads.collect{|ra| CanvasSize.width_to_left_of_tail(ra)}
+    rights.collect!{|r| r+mid.right_required_width} if mid
+    lefts + rights
+  end
+
+  def initialize_with_measures(left,right,height)
+    self.left_width = left || 0.0
+    self.right_width=right || 0.0
+    self.height=height || 0.0
+  end
+  
+  def total_width
+    @left_width+@right_width
+  end
+  
+  def required_width
+    [total_width, HORIZONTAL_NODE_SEPARATION].max
+  end
+  
+  def left_required_width
+    [@left_width, HORIZONTAL_NODE_SEPARATION*0.5].max
+  end
+  
+  def right_required_width
+    [@right_width, HORIZONTAL_NODE_SEPARATION*0.5].max
+  end
+  
+  def height_with_spacing
+    @height + VERTICAL_NODE_SEPARATION
+  end
+  
+  def max_of_each_dimension!(other_canvas_size)
+    @left_width = [@left_width,other_canvas_size.left_width].max
+    @right_width = [@right_width,other_canvas_size.right_width].max
+    @height = [@height,other_canvas_size.height].max
+  end
+  
+  def self.node_separation(direction)
+    case direction
+    when :vertical then VERTICAL_NODE_SEPARATION
+    when :horizontal then HORIZONTAL_NODE_SEPARATION
+    else VERTICAL_NODE_SEPARATION
+    end
+  end
+end

File GUI/src/panels/quantum_stack/descriptor/abstract_descriptor_painter.rb

View file
 
   alias :image_of_model :imageOfModel
 
-  def paintModel(g, p)
+  def paintModel(g)
     #Not to be used
     raise RuntimeError, "do not call paintModel on descriptors, use paintModelAtPoint"
   end
 
   alias :paint_model :paintModel
 
-  def paintModelAtPoint(g,p,center)
+  def paintModelAtPoint(g,center)
     draw_colour_filled_shape(g,my_shape(center), my_colour)
     draw_text_to_left_of_point(g,"#{@model_element.name}",Point.new(center.x-node_size, center.y)) if @model_element.name
     draw_text_centered_at_point(g,"#{@model_element.value}",Point.new(center.x, center.y+node_size)) if @model_element.length == 0
     height  += valsize.height + half_node_size if @model_element.length == 0
     right_width   =  [right_width,valsize.width*0.5].max
     left_width = [left_width,valsize.width*0.5].max
-    {:left => left_width, :right => right_width, :height => height}
+    CanvasSize.new_with_measures(left_width, right_width, height)
   end
 
 

File GUI/src/panels/quantum_stack/quantum_stack_painter.rb

View file
     bistart = BufferedImage.new(10,10,BufferedImage::TYPE_4BYTE_ABGR)
     gstart = bistart.create_graphics
     image_size = model_paint_size(gstart)
-    bifull = BufferedImage.new(image_size[:left] + image_size[:right]+40,image_size[:height]+40,BufferedImage::TYPE_4BYTE_ABGR)
+    bifull = BufferedImage.new(image_size.required_width+40,image_size.height+40,BufferedImage::TYPE_4BYTE_ABGR)
     g = bifull.create_graphics
-    paint_model(g,nil)
+    paint_model(g)
     ImageIcon.new(bifull)
   end
 
   alias :image_of_model :imageOfModel
 
-  def paintModel(g, p)
+  def paintModel(g)
     g.set_rendering_hint(RenderingHints::KEY_ANTIALIASING, RenderingHints::VALUE_ANTIALIAS_ON)
 
     d = model_paint_size(g);
-    paint_model_at_point(g,p,Point.new(d[:left]+20.0,20.0))
+    paint_model_at_point(g,Point.new(d.left_required_width+20.0,20.0))
   end
 
   alias :paint_model :paintModel
 
-  def paintModelAtPoint(g,p,center)
+  def paintModelAtPoint(g,center)
     if model_element.bottom?
       draw_text_centered_at_point(g,"...", center)
     else
       paint_substacks(center,g)
-      @descriptor_painter.paint_model_at_point(g,p,center)
+      @descriptor_painter.paint_model_at_point(g,center)
 
     end
   end
 
   # end of painter interface
 
+  def bottom_element_size(g)
+    dim= get_string_size(g,"...")
+    @preferred_size = CanvasSize.new_with_measures(dim.width * 0.5, dim.width * 0.5, dim.height)
+    return @preferred_size
+  end
+  
   def model_paint_size(g)
-    #todo - refactor to make "size" a class
     return @preferred_size if @preferred_size
-    if model_element.bottom?
-      dim= get_string_size(g,"...")
-      @preferred_size = {:right => (dim.width * 0.5), :left => (dim.width * 0.5), :height => dim.height}
-      return @preferred_size
-    end
-    left_width = 0.0
-    right_width = 0.0
-    height = 0.0
-    mid_point = (@sstack_painters.length - 1) * 0.5
-    @sstack_painters.each_with_index do |sstack_painter,i|
-      dimsstack = sstack_painter.model_paint_size(g)
-      if i < mid_point
-        left_width += [dimsstack[:left] + dimsstack[:right], node_separation(:horizontal)].max
-      elsif i > mid_point
-        right_width += [dimsstack[:left] + dimsstack[:right], node_separation(:horizontal)].max
-      else # i = mid_point
-        left_width += [dimsstack[:left], node_separation(:horizontal) *0.5].max
-        right_width += [dimsstack[:right], node_separation(:horizontal) *0.5].max
-      end
-      height = [height, dimsstack[:height] + node_separation(:vertical)].max
-    end
-
-    if @descriptor_painter
-      dps = @descriptor_painter.model_paint_size(g)
-      left_width = [left_width,dps[:left]].max
-      right_width = [right_width,dps[:right]].max
-      height = [height, dps[:height]].max
-    end
-    @preferred_size = {:left => left_width, :right => right_width, :height => height}
+    return bottom_element_size g if model_element.bottom?
+    
+    
+    @preferred_size = CanvasSize.new_from_subtree (@sstack_painters.collect { |painter| painter.model_paint_size(g)})
+    
+    @preferred_size.max_of_each_dimension!(@descriptor_painter.model_paint_size(g)) if @descriptor_painter
+ 
     @preferred_size
   end
 
-
+  def substack_label(index)
+    "#{@model_element.descriptor.substack_labels[i]}"
+  end
+  
+  def substack_label_placement(index)
+    PLACEMENTS[index <=> (@model_element.substacks.length - 1) * 0.5]
+  end
+  
   def paint_substacks(top_point,g)
-    sign_control = (@model_element.substacks.length - 1) * 0.5
-    sizes = QuantumStackPainter::compute_offsets(basic_sizes(g))
-    @sstack_painters.each_with_index do |sstack_painter,i|
-
-      paint_point = Point.new(top_point.x + sizes[i], top_point.y+node_separation(:vertical))
-
-      draw_black_line(g,top_point, paint_point)
-
-      draw_sized_text(g,LINE_LABEL_FONT_SIZE,"#{@model_element.descriptor.substack_labels[i]}",top_point, paint_point,PLACEMENTS[i <=> sign_control])
-
-      sstack_painter.paint_model_at_point(g,p,paint_point)
-
+    offsets = CanvasSize::compute_offsets(sub_stack_sizes(g))
+    Range.new(0,@sstack_painters.size).each do |i|
+      paint_at_point = top_point.copy_with_x_and_y_offset(offsets[i], node_separation(:vertical))
+      paint_substack(i,top_point,paint_at_point)
     end
   end
+  
+  def paint_substack(index,top_point,paint_point)
+    draw_black_line(g,top_point, paint_point)
+    draw_sized_text(g,LINE_LABEL_FONT_SIZE,substack_label(index),top_point, paint_point,substack_label_placement(i))
+    @sstack_painters[i].paint_model_at_point(g,paint_point)
 
-  def basic_sizes(g)
-    ret = []
-    @sstack_painters.each do |sstack_painter|
-      sstack_size = sstack_painter.model_paint_size(g)
-      ret << [sstack_size[:left], node_separation(:horizontal) * 0.5 ].max
-      ret << [sstack_size[:right], node_separation(:horizontal) * 0.5].max
-    end
-    ret
   end
 
-  def self.compute_offsets(sizes)
-    return [] if !sizes or sizes.length == 0
-    len = sizes.length
-    return [0] if len == 2
-    add_zero_in_middle = (2 == len %4)
-    actives=sizes[1,sizes.length-2]
-    mid_point = (len - 2 ) / 2
-    bldr = []
-    lower_actives = actives.take(mid_point)
-    lower_actives.each_with_index do |a,i|
-      next if i.odd?
-      bldr << - lower_actives[i,mid_point].inject(0) {|sum,la| sum += la}
-    end
-    bldr << 0 if add_zero_in_middle
-    upper_actives = actives.drop(mid_point)
-    upper_actives.each_with_index do |a,i|
-      next if i.odd? and mid_point.odd?
-      next if i.even? and mid_point.even?
-      bldr << upper_actives[0,i+1].inject(0) {|sum,la| sum += la}
-    end
-    bldr
+  def sub_stack_sizes(g)
+    @sstack_painters.collect {|ret,sstack_painter| sstack_painter.model_paint_size(g)}
   end
 
+ 
 
 end

File GUI/src/utility/drawing.rb

View file
 
 java_import javax.swing.ImageIcon
 
-VERTICAL_NODE_SEPARATION=50.0
-HORIZONTAL_NODE_SEPARATION=55.0
+
 
 module Drawing
   def draw_text_centered_at_point(g,text, point)
     g.draw shape
   end
 
-
-  def node_separation(direction)
-    case direction
-    when :vertical then VERTICAL_NODE_SEPARATION
-    when :horizontal then HORIZONTAL_NODE_SEPARATION
-    else VERTICAL_NODE_SEPARATION
-    end
-  end
-
-
-
-
 end

File GUI/src/utility/monkey/array.rb

View file
+class Array
+  def partion_into_left_mid_right
+    [get_left_partition , _get_middle_element_as_array, get_right_partition]
+  end
+  
+  def get_left_partition
+    self[0,self.size/2]
+  end
+  
+  def get_right_partition
+    self[(-(self.size/2)).ceil,self.size/2]
+  end
+  
+  def get_middle_element
+    _get_middle_element_as_array[0]
+  end
+  
+  def tails
+    Range.new(0,(self.size-1)).collect do |i|
+      self[i,self.size-i]
+    end
+  end
+  
+  def heads
+    Range.new(0,(self.size-1)).collect do |i|
+      self[0,i+1]
+    end
+  end
+  
+  private
+  def _get_middle_element_as_array
+    self[self.size/2,-((-self.size/2).ceil+(self.size/2.floor))]
+  end
+  
+end

File GUI/src/utility/monkey/point.rb

View file
+class Point
+  def copy_with_x_and_y_offset(xoffset,yoffset)
+    Point.new(self.x+xoffset, self.y+yoffset)
+  end
+end