UnleashCharts.rb
#-------------------------------------------------------------------------------
# UnleashCharts   Free Ruby Charting library
#	Histograms , X-Y plots, Time Series charts
#	
# Copyright (C) 2005, Unleash Networks P Ltd, All rights reserved
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#--------------------------------------------------------------------------------
module UnleashCharts
 
USECPERSEC=1000000
MSECPERSEC=1000
 
#
# UnChartBase - Base Class for all Charts
#
class UnChartBase < FXCanvas
 
	def initialize(parent)
 
		# parent
		super(parent,nil,0,LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
 
		# defaults
		@backColor = "black"
		@drawColor = "white"
		@font = FXFont.new(getApp(), "verdana", 8, FONTWEIGHT_NORMAL)
		@fonttitle = FXFont.new(getApp(), "verdana", 10, FONTWEIGHT_BOLD)
		@fontsmall = FXFont.new(getApp(), "verdana", 6, FONTWEIGHT_NORMAL)
		@title=nil
 
		# connect events to this canvas
		self.connect(SEL_PAINT) do |sender,sel,event|
			procPaint(sender,sel,event)
		end
 
	end
 
	def create
		super
		@font.create
		@fontsmall.create
		@fonttitle.create
	end	
 
 
	def setModel (mod)
		@model = mod
		update
		repaint
	end
 
 
	def drawGrid(hdc, crect, nlines)
 
		oldstyle=hdc.lineStyle
		oldcolor=hdc.foreground
 
		hdc.lineStyle = LINE_ONOFF_DASH
		hdc.foreground = FXRGB( 128,128,128)
 
		yinc = crect.h/nlines
		ypos = crect.y
		(1..nlines).each do |lnum|
 
			hdc.drawLine( crect.x, ypos, crect.x+crect.w,ypos)
			ypos+=yinc
		end
 
		hdc.lineStyle=oldstyle
		hdc.foreground=oldcolor
 
	end
 
	def drawTitle(dc, crect)
 
		if !@title
			return
		end
 
		dc.font = @fonttitle
		tw=@fonttitle.getTextWidth(@title)
		dc.drawText((crect.w - tw)/2, 20, @title)
 
	end
 
 
private
	def setscales( gridrect , max_x, min_y, max_y )
		ra = RangeAdjuster.new
 
		#print "input model min_y = #{min_y}\n"
 
		max_y=ra.adjustMax(min_y,max_y+ max_y*5/100)
		min_y=ra.adjustMin(min_y,max_y+ max_y*5/100)
 
		#print "adj max_y = #{max_y}\n"
		#print "adj min_y = #{min_y}\n"
 
		@scale_x = (gridrect.w.to_f/max_x.to_f)
		@scale_y = (gridrect.h.to_f/(max_y.to_f-min_y.to_f) )
 
		@yscalelabels=ra.getLabels(min_y,max_y,5)
		# @yscalelabels.each { |s| print "#{s}\n" }
 
		@maxadj_y = max_y
		@minadj_y = min_y
 
	end
 
	def scalex(val)
		val * @scale_x
	end
 
	def scaley(val)
		(val - @minadj_y) * @scale_y
	end
 
	# public member attributes
public
        attr_accessor     :backColor , :drawColor, :barColor, :font
	attr_accessor     :model 
	attr_accessor     :title
 
	# instance variables local
protected
	@scale_x
	@scale_y
	@maxadj_y
	@minadj_y
	@yscalelabels
	@fontsmall
 
end
 
 
#
# A Bar Chart
#
class UnBarChart < UnChartBase
 
	def initialize(parent)
		super(parent)
 
		@barColor="yellow"
	end
 
	def procPaint(sender,sel,event)
 
		dc = FXDCWindow.new(self)
 
 
		# --------------------
		# clear the background
		# --------------------
		dc.foreground = @backColor
		dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)
 
 
		# axis
		dc.foreground = @drawColor
		crect = event.rect
		crect.shrink!(40,30)
		dc.drawLine(crect.x,crect.y+crect.h,crect.x+crect.w,crect.y+crect.h)  # x axis
		dc.drawLine(crect.x,crect.y,crect.x,crect.y+crect.h)          # y axis
 
		if !@model
			dc.end
			return
		end
 
		# set scales
		setscales(crect, @model.barcount, 0, @model.maxval)
 
		# draw scales
		# x-scale
		dc.font = @fontsmall
		@model.each_label_x do |labstr,labval|
			dc.drawText( crect.x + scalex(labval)-5 , crect.y + crect.h + 15, labstr)
		end	
 
		# y-scale
		yval=0
		ystep=@maxadj_y.to_f/5
		@yscalelabels.each do |lbl|
			dc.drawText( crect.x - 30, crect.y+crect.h-scaley(yval), lbl)
			yval += ystep
		end
 
		# draw grid
		drawGrid(dc,crect,5)
 
		# draw title
		drawTitle(dc,crect)
 
		# draw values
		dc.font = @font
		@model.each_val do |xval, yval|
			xpos = crect.x + scalex(xval)
			ypos = scaley(yval)
 
			dc.foreground = @barColor
			dc.fillRectangle(xpos,crect.y+crect.h-ypos,30,ypos)
 
			dc.foreground = @drawColor
			dc.drawText(xpos+5,crect.y+crect.h-ypos-5,yval.to_s)
		end
 
		dc.end
	end
 
public
		attr_writer :barColor 
 
end
 
 
#
# Data Point Style
#
class DataPointStyle
public
	LINEHEIGHT    = 0
	POINTCHAR    = 1
	LINECONNECT = 2
	LINECHAR	   = 3
 
	attr_accessor :type
	attr_accessor :point_char
	attr_accessor :color
 
	def initialize
		@type = LINEHEIGHT
		@pointchar = "o"	
		@color = nil
	end
 
	def initialize( ty, pc)
		@type = ty
		@point_char = pc	
		@color = nil
	end
 
	def initialize( ty, pc, col = nil)
		@type = ty
		@point_char = pc	
		@color = col
	end
 
end
 
 
#
# A Time Series Chart
#
class UnTimeSeriesChart < UnChartBase
 
	def initialize(parent)
		# parent
		super(parent)
	end
 
	def procPaint(sender,sel,event)
 
		dc = FXDCWindow.new(self)
 
		# --------------------
		# clear the background
		# --------------------
		dc.foreground = @backColor
		dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)
 
		# axis
		dc.foreground = @drawColor
		crect = FXRectangle.new(0,0,sender.width,sender.height)
		crect.shrink!(40,30)
		dc.drawLine(crect.x,crect.y+crect.h,crect.x+crect.w,crect.y+crect.h)  # x axis
		dc.drawLine(crect.x,crect.y,crect.x,crect.y+crect.h)          # y axis
 
		if !@model
			dc.end
			return
		end
 
 
		# set scales x (total time ) and y (max y value)
		setscales(crect, @model.totalTimeUSecs,@model.minval, @model.maxval)
 
 
		# draw scales
		# x-scale
		drawTimeScale( dc, crect)
 
 
 
		# y-scale labels from min to max
		yval=@minadj_y
		ystep=(@maxadj_y.to_f-@minadj_y.to_f)/5
		@yscalelabels.each do |lbl|
			use_lab = lbl + @model.valunits
			use_lab.gsub!("Ku","m")
			use_lab.gsub!("Mu"," ")
 
			dc.drawText( crect.x - 30, crect.y+crect.h-scaley(yval), use_lab)
			yval += ystep
		end
 
		# draw grid
		drawGrid(dc,crect,5)
 
		# draw title
		drawTitle(dc,crect)
 
		# draw values
		dc.font = @font
		startTime = @model.startTime
		startTimeUSecs = startTime.tv_sec*USECPERSEC+startTime.tv_usec
		dc.foreground = @drawColor
		oldxpos,oldypos = 0,0
		@model.each_val do |timeval, yval, style|
			xpos = crect.x + scalex((timeval.tv_sec*USECPERSEC+timeval.tv_usec) - startTimeUSecs)
			ypos = scaley(yval)
 
			if style 
				if style.color
					oldclr = dc.foreground
					dc.foreground = style.color
				end
 
				if style.type == DataPointStyle::LINEHEIGHT
					dc.drawLine(xpos,crect.y+crect.h,xpos,crect.y+crect.h-ypos)
				elsif style.type == DataPointStyle::POINTCHAR
					dc.drawText(xpos,crect.y+crect.h-ypos, style.point_char)
				elsif style.type == DataPointStyle::LINECONNECT
					if oldxpos + oldypos > 0
						dc.drawLine(oldxpos,oldypos,xpos,crect.y+crect.h-ypos)
					end
				elsif style.type == DataPointStyle::LINECHAR
					if oldxpos + oldypos > 0
						dc.drawLine(oldxpos,oldypos,xpos,crect.y+crect.h-ypos)
						dc.drawText(xpos,crect.y+crect.h-ypos+5, style.point_char)
					end
				end
 
				if style.color
					dc.foreground = oldclr
				end
			else
				# the default style is a line 
				dc.drawLine(xpos,crect.y+crect.h,xpos,crect.y+crect.h-ypos)
			end
 
			oldxpos,oldypos =xpos,crect.y+crect.h-ypos
 
		end
		# label the axes
		dc.drawText(5,15,@model.yscalelabel)
		dc.drawText(crect.x+crect.w-10, crect.y+crect.h+20,@model.xscalelabel)
 
		dc.end
	end
 
 
	def setModel (mod)
		@model = mod
		update
		repaint()
	end
 
	def drawTimeScale(dc,crect)
		dc.font = @fontsmall
		firstLabelLine1 = @model.startTime.strftime("%x")
		firstLabelLine2 = @model.startTime.strftime("%I:%M:%S%p-")
		firstLabelLine2 += @model.startTime.tv_usec.to_s
		dc.drawText( crect.x - 30, crect.y+crect.h+ 12, firstLabelLine1)
		dc.drawText( crect.x - 30, crect.y+crect.h+ 20, firstLabelLine2)
 
		# right now very simple , just try from a preset list of scales for a match
		candidate_scales =  [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000]
		multiplier = 1
		scale_lab = "us"
		nTicks = 5
		intervalUS = @model.totalTimeUSecs
		if intervalUS / USECPERSEC > nTicks
			scale_lab = "s"
			multiplier = USECPERSEC
		elsif intervalUS / MSECPERSEC > nTicks
			scale_lab = "ms"
			multiplier = MSECPERSEC
		end
 
		use_scale=1
		candidate_scales.each do |s|
			nt = intervalUS / (s * multiplier)
			if use_scale==1 && nt <= nTicks
				use_scale = s
			end			
		end
 
 
		currUS = 0
		i =0
		while currUS <= intervalUS
			xpos = crect.x + scalex(currUS)
			ypos = crect.y + crect.h + 15
			tick = use_scale * i
			lab = "+ #{tick} #{scale_lab}"
 
			if i > 0
				dc.drawText(xpos,ypos,lab)
			end
 
			currUS = currUS + use_scale * multiplier 
			i = i + 1
		end
 
 
 
	end
 
 
 
end
 
 
class RangeAdjuster
 
	def adjustScales(min, max)
		dmin=adjustMin(min,max)
		dmax=adjustMax(min,max)
		min,max=dmin,dmax
	end
 
 
	def getLabels(min,max,labelstep)
		vLabels = Array.new
 
		famax  = adjustMax(min,max).to_f
		famin  = adjustMin(min,max).to_f
 
		fastep = (famax-famin)/labelstep
 
		(0..labelstep).each do |i|
			vLabels<<getFormatted( famin+fastep*i)
		end
 
		getLabels=vLabels
	end
 
	def getFormatted(dval)
		unit=" ";
		dchop=dval
		fmt=""
 
		if (dval>1000000000)
			unit,dchop ="G",(dval/1000000000).to_f
		elsif (dval > 1000000)		
			unit,dchop ="M",(dval/1000000).to_f
		elsif (dval > 1000)		
			unit,dchop ="K",(dval/1000).to_f
		end
 
		if (dchop.to_i*100) == (dchop.to_f*100)
			fmt=sprintf("%u%s",dchop,unit)
		elsif (dchop.to_i*10) == (dchop.to_f*10)
			fmt=sprintf("%.1f%s",dchop,unit)
		else
			fmt=sprintf("%.1f%s",dchop,unit)
		end
 
 
		getFormatted=fmt
	end
 
 
	def adjustMax(dmin,dmax)
		mul=1.0
 
		if dmax==dmin
			dmax=dmin+1
		end
 
		#print "adjustMax dmax = #{dmax} dmin = #{dmin}\n"
 
		if dmax > 1000000000
			dmax = (dmax.to_f / 1000000000)
			dmin = dmin.to_f   / 1000000000
			mul  = 1000000000
		elsif dmax > 1000000
			dmax = (dmax.to_f / 1000000)
			dmin = dmin.to_f / 1000000
			mul  = 1000000
		elsif dmax > 1000
			dmax = (dmax.to_f / 1000)
			dmin = dmin.to_f / 1000
			mul  = 1000
		end
 
		getCeil(dmax, 10** getOrder(dmax-dmin)) * mul
	end
 
 
	def adjustMin(dmin,dmax)
		mul=1.0
 
		if dmax-dmin < 10
			return dmin * 10
		end
 
		if dmin > 1000000000
			dmax = dmax.to_f / 1000000000
			dmin = dmin.to_f / 1000000000
			mul  = 1000000000
		elsif dmin > 1000000
			dmax = dmax.to_f / 1000000
			dmin = dmin.to_f / 1000000
			mul  = 1000000
		elsif dmin > 1000
			dmax = dmax.to_f / 1000
			dmin = dmin.to_f / 1000
			mul  = 1000
		end
 
		adjustMin =  getFloor(dmin, 10**getOrder(dmax-dmin)) * mul
 
	end
 
 
 
	def getOrder(d)
		if d<=0
			0
		else
			Math.log10(d).floor
		end
	end
 
	def getFloor(d,l)
		v = d/l
		l * v.floor
	end
 
	def getCeil(d,l)
		v = (d.to_f/l.to_f).ceil
 
		#print "getceil d=#{d} v=#{v} l=#{l}\n"
		l * v
	end
 
end # of class RangeAdjuster
 
 
end # of module UnleashCharts
unsniff/samples/unleashcharts/ruby.txt · Last modified: 2014/09/11 23:23 (external edit)
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki