PlotGraph

Post Reply
User avatar
Dutchman
Posts: 851
Joined: Mon May 06, 2013 9:21 am
My devices: iMac, iPad Air, iPhone
Location: Netherlands
Flag: Netherlands

PlotGraph

Post by Dutchman »

PlotGraph is a set of functions for plotting data.
There are two major functions PGpreset(…) and PG(…)
The system should be initialised with a call to PGpreset(…) which set the parameters for the template PG(…)
A call to PG(…) will generate the template. If this is done within a sprite, then the graph can simply be drawn on a different background, can be moved, stored etc.

Code: Select all

'PlotGraph by Dutchman, august 2017
'Plot functions and testprogram
'
'==== Constants ====
Data$="Function y=SIN(x)/x with x in radians. (- - = average ± sigma)"
'==== PRESETS
OPTION BASE 1
GET SCREEN SIZE sw,sh
'
'==== initiate data
nd=2000 ! sum=0
mid=nd/2
Phi.mid=mid 'presets in function Phi
Phi.dphi=ACOS(-1)/50
DIM Data(nd)
FOR i=1 TO nd
  Data(i)=Func(Phi(i))
  sum+=Data(i)
NEXT i
'---- determine characteristics
Avg=sum/nd ' average value
GOSUB Analyse 'determine low, high and sigma
'
'==== MAIN ====
GRAPHICS ! GRAPHICS CLEAR 1,1,1'background
'--- define sprite-size
gw=sw-20 ! gh=sh/2.1 ' graph width and height
'b'--- init graph-units for template
fmin=9 'minimum fontsize
i=50   'characters per minimum screenwidth
fsize=CEIL(MAX((MIN(sw,sh)-20)/i,fmin))
CALL PGpreset(1,gw,1,fsize) 'with caption 
pg.Caption$=Data$   'set caption
''
'g'===== GRAPH 1 ======
rp=1 ! gp=0 ! bp=0 ' pen color for curve
'--- set y-range
fymin=FLOOR(5*low)/5 ! fymax=CEIL(5*high)/5
fystep=0.2 'automatic y-labels
'--- set x-zoom-range
start=1 'index to start with
finish=nd 'index to end with
fxmin=Phi(start) ! fxmax=Phi(finish)
fxstep=0 'no automatic x-labels
'--- set label formats
xf$="#" ! yf$="#.#" 'label formats
'--- init sprite nr 1 and plot
nr$="1"
CALL InitSprite(nr$,gw,gh,0.5,0.8,0.5)
SPRITE nr$ AT 10,10
pen=4 'width of curve
GOSUB PlotGraph
'--- set x-labels
DRAW COLOR pg.cr,pg.cg,pg.cb
GOSUB DrawXlabel
''
'--- modify data  with noise
sum=0
FOR i=1 TO nd
  Data(i)*=2*RND(1)-1
  sum+=Data(i)
NEXT i
'---- determine characteristics
Avg=sum/nd ' average value
GOSUB Analyse 'determine low, high and sigma

'g'===== GRAPH 2 ======
Data$="Horizontal zoom on simulated seismographsignal. (- - = average ± sigma)"
pg.Caption$=Data$
rp=1 ! gp=1 ! bp=0 ' pen color for curve
'b'--- change axes color
pg.cr=1 ! pg.cg=1 ! pg.cb=1 ' axes color
'g'--- set y-range
fymin=FLOOR(5*low)/5 ! fymax=CEIL(5*high)/5
fystep=0.2 'automatic y-labels
'--- set x-range and formats
start=INT(mid-nd/10) 'zoom at mid
finish=INT(mid+nd/10) 'symetrical
fxmin=Phi(start) ! fxmax=Phi(finish)
fxstep=0 'no automatic x-labels
xf$="#" ! yf$="#.#" 'label formats
'--- define sprite-size
gw=3*sw/4 ! gh=sh/2.1 ' graph width and height
'--- init sprite nr 1 and plot
nr$="2"
CALL InitSprite(nr$,gw,gh,0,0,1)
SPRITE nr$ AT (sw-gw)/2,sh/2
pen=2 'width of curve
GOSUB PlotGraph
'--- set x-labels
DRAW COLOR pg.cr,pg.cg,pg.cb
GOSUB DrawXlabel
''
DO ! UNTIL 0
END
'
''====== Subroutines and functions ========
PlotGraph:
SPRITE nr$ BEGIN
'b'--- draw template
  CALL PG(gw,gh,fxmin,fxmax,fxstep,xf$,fymin,fymax,fystep,yf$)
''--- position pen
'b'get xy-values and draw
  CALL PGpos(phi(start),Data(start))
  DRAW TO PGpos.x,PGpos.y
''--- draw graph with linesegments
  DRAW SIZE pen
  DRAW COLOR rp,gp,bp
  FOR i=start+1 TO finish
'b'--- draw line-segment
    CALL PGpos(phi(i),Data(i))
    DRAW LINE TO PGpos.x,PGpos.y
''
  NEXT i
  GOSUB Lines 'draw avg and ±sigma
SPRITE nr$ END
RETURN
'
DrawXlabel:
SPRITE nr$ BEGIN
  fxmin=10*CEIL(Phi(start)/10)
  fxmax=10*FLOOR(Phi(finish)/10)
  fxstep=INT((fxmax-fxmin)/INT((fxmax-fxmin)/10))
  IF (fxmax-fxmin)/fxstep<5 THEN fxstep/=5
  FOR i=fxmin TO fxmax STEP fxstep
    CALL PGlabel(i,STR$(i,xf$),1)
  NEXT i
SPRITE nr$ END
RETURN
'
Lines: 'draw avg,avg+sigma,avg-sigma
DRAW COLOR pg.cr,pg.cg,pg.cb
DRAW DASH 5
DRAW SIZE 1
'--- set fxmin and fxmax
CALL PGpos(Phi(start),avg) ! xmin=PGpos.x
CALL PGpos(Phi(finish),avg)! xmax=PGpos.x
'--- draw average
DRAW LINE  xmin,PGpos.y TO xmax,PGpos.y
'--- average±sigma
PGpos(fxmin,avg+sigma)
DRAW SIZE 1
DRAW LINE xmin,PGpos.y TO xmax,PGpos.y
PGpos(fxmin,avg-sigma)
DRAW SIZE 1
DRAW LINE  xmin,PGpos.y TO xmax,PGpos.y
'--- draw zero y
DRAW COLOR rp,gp,bp
DRAW DASH 0
CALL PGpos(fxmin,0)
DRAW LINE  xmin,PGpos.y TO xmax,PGpos.y
RETURN
'
DEF InitSprite(nr$,w,h,br,bg,bb)
SPRITE nr$ BEGIN w,h
  GRAPHICS ! GRAPHICS CLEAR br,bg,bb
SPRITE END
SPRITE nr$ AT 0,0
SPRITE nr$ SHOW
END DEF
'
DEF Phi(index)' make phase from index
'-- requires preset of mid of index-range and dphi
  x=(index-mid)*dphi
RETURN x
END DEF
'
DEF Func(phase)'return function of phase
  IF phase=0 THEN ! fy=1
  ELSE ! fy=SIN(phase)/phase ! ENDIF
RETURN fy
END DEF
'
Analyse: 'determine low, high and sigma
low=Data(1) ! high=Data(1) 'presets
sigma=0
FOR i=1 TO nd
  x=Data(i)
  low=MIN(low,x) ! high=MAX(high,x)
  sigma+=(x-avg)^2
NEXT i
sigma=SQR(sigma/nd)
RETURN
'
'r'============ PlotGraph functions =================
'by Dutchman, august 2017
/* See test-program "PlotGraph test.sb"
Functions should be initialised with a call to PGpreset(…)
which set the parameters for the template PG(…)
Default values can be changed by direct setting e.g. PG.dash=15
A call to PG(…) will generate the template.
If this is done within a sprite, then the graph can
simply be drawn on a different background, can be moved etc.
For drawing curves on the template, the function PGpos(…) can
be used to generate pixel-x,y values from function-fx,fy values.
If a step-parameter dx and/or dy in the function PG(…) is non-zero,
then the corresponding label(s) will automatically be generated,
else the labels can be drawn with the function PGlabel(…)
See headers in the function-definitions for further details
*/
DEF PGpos(fx,fy)
'transform function(fx,fy)-values to screen(x,y)-pixels
x=pg.xzero+fx*pg.xmult
y=pg.yzero+fy*pg.ymult
END DEF
'
DEF PGlabel(xy,text$,horizontal)
'draw label to axis
'if horizontal=1 then labels are drawn to x-axis
'  else to y-axis
DRAW SIZE pg.pen
IF horizontal THEN
  x=pg.xzero+xy*pg.xmult ' x-plotposition
  DRAW LINE x,pg.y0 TO x,pg.y0+pg.dash
  DRAW TEXT text$ AT x,pg.y0+pg.dash+pg.fs/2+1
ELSE
  y=pg.yzero+xy*pg.ymult ' y-plotposition 
  DRAW LINE pg.x0,y TO pg.x0-pg.dash,y
  DRAW TEXT text$ AT pg.x0-pg.dash-1-TEXT_WIDTH(text$)/2,y
ENDIF
END DEF
'
DEF PG(w,h,xmin,xmax,dx,xf$,ymin,ymax,dy,yf$)
'init graph and units for template
'xf$ and yf$ are format$'s for axis-labels, e.g. "#.#"
'following variables to be set e.g. by PGpreset(…)
'  fs,lm,bm,tm,pen,cr,cg,cb
'function-point fx,fy can be adressed as:
'    DRAW TO pg.xzero+fx*pg.xmult, pg.yzero+fy*pg.ymult 
IF fs=0 THEN CALL PGpreset(0,w,1,15)'not initialised
x0=lm ! y0=h-bm           'lower left corner of graph
yrange=y0-tm              'size of y-axis
ymult=-yrange/(ymax-ymin) 'y-multiplier for plot
yzero=y0-ymin*ymult       'zerovalue for plot
xrange=w-x0-rm            'size of x-axis
xmult=xrange/(xmax-xmin)  'x-multiplier for plot
xzero=x0-xmin*xmult       'zero-value for plot
Axes:
OPTION TEXT POS CENTRAL
DRAW FONT SIZE fs
DRAW FONT NAME Font$
DRAW SIZE pen
DRAW COLOR cr,cg,cb 
'---- draw vertical axis
DRAW LINE x0,y0 TO x0,y0-yrange  ' vertical
IF dy<>0 THEN
FOR i=ymin TO ymax STEP dy
  t$=STR$(i,yf$)
  CALL PGlabel(i,t$,0) 'draw y-label
NEXT i
ENDIF
'---- draw horizontal axis
DRAW LINE x0,y0 TO w-rm,y0 ' horizontal
IF dx<>0 THEN
FOR i=xmin TO xmax STEP dx
  t$=STR$(i,xf$)
  CALL PGlabel(i,t$,1) 'draw x-label
NEXT i
ENDIF
IF caption$<>"" THEN
  x=x0+xrange/2
  DRAW TEXT caption$ AT x,y0+dash+1.5*fs+2
ENDIF
END DEF
'
DEF PGpreset(style,width,font,fs)
' set parameters for plot-template PG(…)
' style facilitates optional settings
' width is total area-width including margins
' font is number which determines font-name. See DATA
' fs is fontsize
IF NOT set THEN GOSUB Init
IF style=0 THEN ! GOSUB Default
ELSE ! ON Style GOTO s1,s2 'etc.
ENDIF
RETURN
'---- optional changes
s1:' make space for caption$
  pg.bm=3*fs+dash+1 'extended bottom margin
RETURN
s2:
  'to be defined etc.
RETURN
'---- local subroutines
Default:
  nw=5 'maxlength of numbers along y-axis
  ns=TEXT_WIDTH("0123456789")/10 'width of single number
  pg.font$=Font$(font)'see DATA
  pg.fs=fs            'fontsize
  pg.dash=10          'length of grid-dash on axes
  pg.lm=nw*ns+dash    'left margin
  pg.rm=10            'right margin
  pg.bm=2*fs+dash+1   'bottom margin
  pg.tm=20            'top margin, relative
  pg.pen=2            'linewidth of axes
  pg.cr=0!pg.cg=0!pg.cb=0 ' axes color
RETURN'from subroutine
Init:
  fonts=6
  DIM Font$(fonts)
  FOR i=1 TO fonts ! READ Font$(i) ! NEXT i
  GOSUB Default
  set=1
RETURN'from subroutine
DATA "ArialMT","AvenirNextCondensed-Regular","Arial-BoldMT"
DATA "AvenirNextCondensed-Bold","Menlo","Menlo-Bold"
END DEF
'
The red section of the code is the PlotGraph system and can be separated into a lib-file.
Further details are given in that section.
The rest of the code is for testing and generates the following screenshots.
The display on iPhone could be optimised by adapting draw size etc.
-
screenshots.PNG
screenshots.PNG (283.61 KiB) Viewed 3007 times

Post Reply