# # GENERIC CLASS # Author:: Mark Harrison-Ball # Date:: 20 Februray 2013 (AP3) # Purpose: # Draw a graph Utility # require 'System.Drawing' path = File.expand_path $0 path = File.dirname(path) require "#{path}/../../Global/image/Image.rb" require "#{path}/../../Global/array/arrayUtils.rb" class Graph attr_reader :image attr_reader :graphics #attr_reader :defaultPenColor attr_reader :pen def initialize( x , y ) #Image @x = x @y = y #margin @margin = 20 #Graph @gX = x-(@margin*2) @gY = y-(@margin*2) # Brush List @BrushBeige = System::Drawing::SolidBrush.new(System::Drawing::Color.FromArgb(243,237,237)) @BrushGrey = System::Drawing::SolidBrush.new(System::Drawing::Color.FromArgb(160,160,160)) @BrushBlack = System::Drawing::SolidBrush.new(System::Drawing::Color.Black) @BrushDarkGrey = System::Drawing::SolidBrush.new(System::Drawing::Color.FromArgb(150,150,150)) # Pen List @PenBeige = System::Drawing::Pen.new(System::Drawing::Color.FromArgb(223,217,217)) @PenGrey = System::Drawing::Pen.new(System::Drawing::Color.FromArgb(97,95,95)) @PenBlack = System::Drawing::Pen.new(System::Drawing::Color.Black) @PenRed = System::Drawing::Pen.new(System::Drawing::Color.Red) @PenGreen = System::Drawing::Pen.new(System::Drawing::Color.Green) @PenBlue = System::Drawing::Pen.new(System::Drawing::Color.Blue) @PenPurple = System::Drawing::Pen.new(System::Drawing::Color.Purple) # Font List @DrawFont = System::Drawing::Font.new("Arial", 6) # Pen Array List @penList = [@PenRed, @PenGreen, @PenBlue, @PenPurple] @gWidth = 0 @gHeight = 0 @marginX = 20 @marginY = 20 # Used for negative values @setMinHOffset = 0 #fillbackground() @Title = "" @arrayUtils = ArrayUtils.new() @stringformat = System::Drawing::StringFormat.new() @right = System::Drawing::StringAlignment.Far @center = System::Drawing::StringAlignment.Center @left = System::Drawing::StringAlignment.Near @stringformat.Alignment = @right end def path @image.path() end def new(path) @image = Image.new( @x , @y, path ) @graphics = System::Drawing::Graphics.from_image(@image.source) fillbackground() end def save() @image.save() end # draw a list of blocks # block list is a list of pairs # timecode is based on 0 -1 def drawblocks(blocklist) blocklist.each do | block | x1 = @x * block[0].to_f x2 = @x * block[1].to_f x3 = x2-x1 if x3 < 1 then x3 = 1 end drawtext('B', x1, 9, @center) drawFillRectangle(@BrushDarkGrey, x1, (@margin/2)+5, x3, @gY+(@margin/2)-5) end end # a Array of points # Confim how many vectoras and draw all of them # RED, GREEN and BLUE def drawgraph(vectorArray, title = "") @Title = title #puts " Procewsing data: #{vectorArray} " @gWidth = getMaxWidth(vectorArray) @gHeight = setMaxHeight(vectorArray) drawGraphInfo(vectorArray) #puts " Drawn Graph Info" # If vector puts "Vecotr is a #{vectorArray[0].class}" if vectorArray[0].class == Array then #puts 'Is an Array' (0..vectorArray[0].size-1).each do |x| tempArray = Array.new() (0..vectorArray.size-2).each do |n| tempArray << vectorArray[n][x] end draw(tempArray, x) end else # draw liner #puts 'Not a Array, prob FOV' #puts vectorArray.min #puts vectorArray.max draw(vectorArray, 0) end end #private def header() drawFillRectangle(@BrushGrey, 0,0, @x, 14) drawtext("BLOCKING TAGS", @x/2,0) end def drawgrid(array) # work out steps for every 10 maxSize = array.size # Set our Fontheigh here in the future fontHeight = -5 stepX = @gX.to_f / maxSize.to_f stepY = @gY.to_f / maxSize.to_f #puts "stepX = #{stepX}" # Horizitonal Gird and numbers stepMultiplier = @gY.to_f/maxSize.to_f #puts "stepMultiplier = #{stepMultiplier}" mStep = maxSize/30 (0..maxSize).step(mStep) { |n| drawline(@PenBeige, @margin+(stepX*n), @margin, @margin+(stepX*n), @gY+@margin) drawtext(n, @margin+(stepX*n), @gY+@margin+4, @center) } drawline(@PenBeige, @gX.to_f+@margin-1, @margin, @gX.to_f+@margin-1, @gY+@margin) # BIt of a cheat as we daw the last grid line 1 pixel in so its drawn on the screen #drawtext(maxSize, @gX.to_f+@margin, @gY+@margin+4, @center) #drawtext("frames", ((@margin+(@gX/2))-mStep),(@y-12)) # Verticl Gird and numbers # Get our min max length range = @arrayUtils.retRange(array) #puts "range = #{range}" #puts "Min = #{@arrayUtils.retMin(array)}" #puts "Max = #{@arrayUtils.retMax(array)}" # Mininmun number of Grid divisions gridStepDivision = 10 # Check if we have a range, if not then the graph range difference is 0 and nothing has changed if range > 0 then # Divid our range by our bitmap display height size (@gY) to get our step multiplier stepMultiplier = @gY.to_f/range.to_f # Set our step Grid Y Size mStep = range/gridStepDivision #puts "mStep = #{mStep}" # Get our offset if numbers are negative #yOffset = @arrayUtils.retMin(array).abs yOffset = @arrayUtils.retMin(array) * -1 #puts "ABS of #{@arrayUtils.retMin(array)} = #{yOffset} " (@arrayUtils.retMin(array)..@arrayUtils.retMax(array)).step(mStep) { |n| yHeight = @margin + ((n+yOffset)*stepMultiplier) drawline(@PenBeige, @margin, yHeight, @gX+@margin, yHeight) value = '%.2f' % n drawtext(value, @margin, yHeight+fontHeight, @right) } value = '%.2f' % @arrayUtils.retMax(array) drawtext(value, @margin, @gY+@margin+fontHeight, @right) else # Draw single line at centre as nothing has changed drawline(@PenBeige, @margin, @margin+(@gY.to_f/2), @gX+@margin, @margin+(@gY.to_f/2)) drawtext(0, @margin, @margin+(@gY.to_f/2)+fontHeight, @right) end # GRID OUTLINES # vertical drawline(@PenBlack, @margin, @gY+@margin, @gX+@margin, @gY+@margin) # Hotizontal drawline(@PenBlack, @margin, @gY+@margin, @margin, @margin) end def drawGraphInfo(vectorArray) drawgrid(vectorArray) header() drawtext(@Title, 20+@margin,20+@margin) end def getMaxHeight(array) return @arrayUtils.retRange(array) end def setMaxHeight(array) min = @arrayUtils.retMin(array) #puts "min = #{min}" # GEt are shift so number is positive #heighShift = min * -1 #startPoint = (max.to_f - min.to_f / 2) + heighShift # GET OUR OFFSET SO ITS STARTS AT 0 @setMinHOffset = 0.0 if min != 0.0 then @setMinHOffset = min * -1 end maxheightRange = getMaxHeight(array) result = @gY.to_f/maxheightRange.to_f #-@margin return result # For nulll chnage if maxheightRange != 0.0 then result = (@y.to_f-@margin)/maxheightRange else # Array does not contain any differences, so draw at 0 center of image @setMinHOffset = (@gY/2) #result = end return result end def getMaxWidth(array) return (@gX.to_f)/(array.size-2).to_f end def fillbackground() @graphics.FillRectangle(@BrushBeige, 0,0, @x, @y) end def draw(pointArray, penIndex) if @arrayUtils.retRange(pointArray) > 0 then (0..pointArray.size-2).each do |n| pen = @penList[penIndex] x1 = @margin+(n*@gWidth) y1 = @margin+((pointArray[n]+@setMinHOffset)*@gHeight) y2 = @gY + @margin y2 = @margin+((pointArray[n+1]+@setMinHOffset)*@gHeight) #draw lin emin and max height x2 = @margin+((n+1)*@gWidth) drawline(pen, x1, y1, x2, y2 ) #drawcircle(pen, x1, y1, 2) end else pen = @penList[0] drawline(pen, @margin,@margin+(@gY/2), @margin+@gX, @margin+(@gY/2) ) end end # draw a line def drawline(penColor, x1, y1, x2, y2) #puts " X1:#{x1} , Y1:#{y1} , X2:#{x2} ,Y2:#{y2}" @graphics.DrawLine(penColor, x1, y1, x2, y2) end # Draw a circle def drawcircle(penColor, x1, y1, d1) @graphics.DrawEllipse(penColor, x1-(d1/2), y1-(d1/2), d1, d1) end # Draw Text def drawtext(text, x, y, align = @left ) @stringformat.Alignment = align @graphics.DrawString(text.to_s, @DrawFont, @BrushBlack, x, y, @stringformat) end # Draw solid rectangel def drawFillRectangle(brushColor, x1, y1, w, h) newRectangle = System::Drawing::Rectangle.new(x1, y1, w, h) @graphics.FillRectangle(brushColor, newRectangle) end end