Creating (scientific) plots via gnuplot

User projects written in or related to FreeBASIC.
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Creating (scientific) plots via gnuplot

Post by badidea »

I was looking for a way to make nice data plots in FreeBASIC. I decided to try via calling gnuplot (http://www.gnuplot.info/). Although still work in process, it is working on my linux laptop, currently only via 64-bit FBC (some issue with zlib and libpng on 32-bit).

The program steps:
1. Write some data to file (in the format that gnuplot expects)
2. Create a gnuplot script file (all the plot settings)
3. Call gnuplot with the script file as argument
4. Open and display the created PNG-file in freebasic.

Code so far:

Code: Select all

'gnuplot wrapper, badidea, 2019-05-15
'libs/tools needed: gnuplot, libcairo, libz, libpng, ... 

#include once "png12.bi"
#include once "fbgfx.bi"
#include once "string.bi"

const SCR_W = 800, SCR_H = 600, SCR_BPP = 32

declare function writeData(fileName as string, header() as string, myData() as double) as integer
declare function imageread_png(byref filename as string, byval bpp as integer) as FB.IMAGE ptr

const LF = !"\n"
const DQ = chr(34)

function quote(str1 as string) as string
	return DQ + str1 + DQ
end function

type range_type
	dim as double min, max
end type

type size_type
	dim as integer x, y
end type

type font_type
	dim as string face
	dim as integer size
	declare constructor(fontFace as string, fontSize as integer)
end type

constructor font_type(fontFace as string, fontSize as integer)
	face = fontFace
	size = fontSize
end constructor

type gnuplot_script
	dim as string setStr, termStr, plotStr
	'labels 
	dim as string graph_title = "<Graph Title>"
	dim as string x_label = "<X label [.]>"
	dim as string y_label = "<Y label [.]>"
	'axis
	dim as integer x_log_scale = 0
	dim as integer y_log_scale = 0
	dim as integer x_auto_scale = 1
	dim as integer y_auto_scale = 1
	dim as range_type x_range = type(-1, +1)
	dim as range_type y_range = type(-1, +1)
	dim as integer show_grid = 1
	'output
	dim as string term_output_file = "gnuplot01.png"
	'terminal
	dim as size_type term_size = type(SCR_W, SCR_H)
	dim as integer term_transp = 0
	dim as font_type term_font = type("arial", 18)
	dim as integer term_crop = 0
	dim as single term_line_width = 3.0
	'plot  
	dim as string dataFile = ""
	dim as string plot_title = "<Plot Title>"
	'... 
	declare sub prepare(fileName as string)
	declare sub write(fileName as string)
	declare sub show()
end type

'-------------------------------------------------------------------------------

const NUM_COL = 3, NUM_ROW = 10

'some random data
dim as double current(NUM_ROW-1) = {120, 125, 122, 119, 134, 138, 141, 159, 162, 178}
dim as double voltage(NUM_ROW-1) = {250, 210, 180, 155, 140, 125, 110, 100, 90, 80}
dim as double resistance(NUM_ROW-1)

dim as string header(NUM_COL-1) = {"I [A]", "U [V]", "R [Ω]"}
dim as double myData(NUM_COL-1, NUM_ROW-1)

for i as integer = 0 to NUM_ROW-1
	myData(0, i) = current(i)
	myData(1, i) = voltage(i)
	myData(2, i) = voltage(i) / current(i)
next

dim as string dataFile = "gpdata.dat" 'plot data
writeData(dataFile, header(), myData())

dim as string scriptFile = "gpscript.p" 'plot settings
dim as gnuplot_script gs

'adjust settings
gs.graph_title = "<My Title>"
gs.x_label = "Current [A]"
gs.y_label = "Voltage [V]"
gs.x_auto_scale = 0
gs.x_range = type(100, 200)
gs.y_auto_scale = 0
gs.y_range = type(0, 300)

gs.prepare(dataFile)
gs.show()
gs.write(scriptFile)

'call gnuplot
shell("gnuplot " + scriptFile)

'load and show the image
screenres SCR_W, SCR_H, SCR_BPP
dim as FB.IMAGE ptr pImage
pImage = imageread_png(gs.term_output_file, SCR_BPP )
if pImage = 0 then
	print "Image error"
else
	windowtitle(gs.term_output_file)
	put (0, 0), pImage, pset 'alpha
	imagedestroy(pImage)
end if

while inkey = "": wend
end 1

'Resource links:
'* http://www.gnuplotting.org/tag/png/
'* https://riptutorial.com/gnuplot/example/27496/fit--basic-linear-interpolation-of-a-dataset

'-------------------------------------------------------------------------------

'creates the gnuplot script. Call before write()
sub gnuplot_script.prepare(fileName as string)
	dataFile = fileName
	setStr = "# Gnuplot script file (http://gnuplot.info/)" + LF
	setStr += "# Generated: " + date() + ", " + time() + LF
	setStr += "set title " + quote(graph_title) + LF
	setStr += "set xlabel " + quote(x_label) + "" + LF
	setStr += "set ylabel " + quote(y_label) + "" + LF
	if x_log_scale = 1 then setStr += "set logscale x" + LF
	if y_log_scale = 1 then setStr += "set logscale y" + LF
	if x_auto_scale = 1 then
		setStr += "set autoscale x" + LF
	else
		setStr += "set xrange [" + str(x_range.min) + ":" + str(x_range.max) + "]" + LF
	end if
	if y_auto_scale = 1 then
		setStr += "set autoscale y" + LF
	else
		setStr += "set yrange [" + str(y_range.min) + ":" + str(y_range.max) + "]" + LF
	end if
	if show_grid = 1 then setStr += "set grid" + LF
	setStr += "set output " + quote(term_output_file) + LF
	'terminal
	termStr = "set terminal"
	termStr += " pngcairo"
	termStr += " size " + str(term_size.x) + "," + str(term_size.y)
	if term_transp = 1 then termStr += " transparent"
	termStr += " font " + quote(term_font.face + "," + str(term_font.size))
	if term_crop = 1 then termStr += " crop"
	termStr += " linewidth " + str(term_line_width)
	'plot
	plotStr = "plot \" + LF
	plotStr += " " + quote(dataFile)
	plotStr += " using 1:2"
	plotStr += " title " + quote(plot_title) 
	plotStr += " with linespoints"
end sub

'write the gnuplot script to file. Call prepare first()
sub gnuplot_script.write(fileName as string)
	dim as integer fileNum = freefile
	if open(fileName, for output, as fileNum) = 0 then
		print #fileNum, setStr
		print #fileNum, termStr
		print #fileNum, plotStr
		close(fileNum)
	else
		print "Error opening: " + quote(fileName)
	end if
end sub

'print the script file to screen, for debugging
sub gnuplot_script.show()
	print setStr
	print termStr
	print plotStr
end sub

'-------------------------------------------------------------------------------

function writeData(fileName as string, header() as string, myData() as double) as integer
	dim as integer fileNum = freefile
	if open (fileName, for output, as fileNum) = 0 then
		for iCol as integer = 0 to ubound(header)
			print #fileNum, header(iCol),
		next
		print #fileNum, "" 'line break
		for iRow as integer = 0 to ubound(myData, 2)
			for iCol as integer = 0 to ubound(myData, 1)
				print #fileNum, str(myData(iCol, iRow)),
			next
			print #fileNum, "" 'line break
		next
		close(fileNum)
	else
		print "Error opening: " + quote(fileName)
		return -1
	end if
	return 0
end function

'-------------------------------------------------------------------------------

'PNG routine from freeBASIC examples (a bit modified)
function imageread_png(byref filename as string, byval bpp as integer) as FB.IMAGE ptr
	
	dim as ubyte header(0 to 7)
	
	dim as FILE ptr pFile = fopen(filename, "rb")
	if pFile = 0 then
		print "could not open image file"
		return 0
	end if
	
	if fread(@header(0), 1, 8, pFile) <> 8 then
		print "couldn't read header"
		fclose(pFile)
		return 0
	end if
	
	if(png_sig_cmp(@header(0), 0, 8)) then
		print "png_sig_cmp() failed"
		fclose(pFile)
		return 0
	end if
	
	dim as png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0)
	if pPng = 0 then
		print "png_create_read_struct() failed"
		fclose(pFile)
		return 0
	end if
	
	dim as png_infop pInfo = png_create_info_struct(pPng)
	if pInfo = 0 then
		print "png_create_info_struct() failed"
		fclose(pFile)
		return 0
	end if
	
	png_init_io(pPng, pFile)
	png_set_sig_bytes(pPng, 8)
	png_read_info(pPng, pInfo)

	dim as integer w, h, bitdepth, channels, pixdepth, colortype, rowbytes
	w = png_get_image_width(pPng, pInfo)
	h = png_get_image_height(pPng, pInfo)
	bitdepth = png_get_bit_depth(pPng, pInfo)
	channels = png_get_channels(pPng, pInfo)
	pixdepth = bitdepth * channels
	colortype = png_get_color_type(pPng, pInfo)

	dim as FB.IMAGE ptr pImg = imagecreate(w, h)
	dim as ubyte ptr pDst = cptr(ubyte ptr, pImg + 1) '???

	png_set_interlace_handling(pPng)
	png_read_update_info(pPng, pInfo)

	rowbytes = png_get_rowbytes(pPng, pInfo)
	dim as ubyte ptr pSrc = callocate(rowbytes)

	for y as integer = 0 to h-1
		png_read_row(pPng, pSrc, 0)

		select case(colortype)
		case PNG_COLOR_TYPE_RGB
			imageconvertrow(pSrc, 24, pDst, bpp, w)
			pDst += pImg->pitch
		
		case PNG_COLOR_TYPE_RGB_ALPHA
			select case(bpp)
			case 24, 32
				for i as integer = 0 to rowbytes-1 step 4
					'' FB wants: &hAARRGGBB, that is &hBB &hGG &hRR &hAA (little endian)
					'' libpng provides &hAABBGGRR, that is &hRR &hGG &hBB &hAA (little endian)
					'' so we need to copy AA/GG as-is, and swap RR/BB
					pDst[0] = pSrc[i+2]
					pDst[1] = pSrc[i+1]
					pDst[2] = pSrc[i+0]
					pDst[3] = pSrc[i+3]
					pDst += 4
				next
			case 15, 16
				'' No alpha supported, only RGB will be used
				imageconvertrow(pSrc, 32, pDst, bpp, w )
				pDst += pImg->pitch
			end select
		
		case PNG_COLOR_TYPE_GRAY
			select case(bpp)
			case 24, 32
				for i as integer = 0 to rowbytes-1
					*cptr(ulong ptr, pDst) = rgb( pSrc[i], pSrc[i], pSrc[i] )
					pDst += 4
				next
			case 15, 16
				for i as integer = 0 to rowbytes-1
					pset pImg, (i, y), rgb( pSrc[i], pSrc[i], pSrc[i] )
				next
			case else
				'' 8 bpp and less require a proper global palette,
				'' which contains the colors used in the image
				'for i as integer = 0 to rowbytes-1
				'	pset pImg, (i, jinfo.output_scanline-1), pSrc[i]
				'next
				memcpy(pDst, pSrc, rowbytes )
				pDst += pImg->pitch
			end select
		end select
	next
	
	deallocate(pSrc)
	
	png_read_end(pPng, pInfo)
	png_destroy_read_struct(@pPng, @pInfo, 0)
	fclose(pFile)
	
	return pImg
end function
Should output:
Image
Things to to:
- Multiple plots in one graph
- Control over the plot colors and markers
- Data fitting
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Creating (scientific) plots via gnuplot

Post by jj2007 »

Carlos Herrera
Posts: 82
Joined: Nov 28, 2011 13:29
Location: Dictatorship

Re: Creating (scientific) plots via gnuplot

Post by Carlos Herrera »

This is a very good idea but I think in general it is better to open gnuplot in persistent mode
and write to its graphics terminal directly, using the Open Pipe mechanism. Gnuplot terminal,
at least on windows, is very impressive (antialiasing, GDI+,...) and very fast.
If an old and buggy fbgfx form is replaced by the gnuplot engine, we will have something
that matches TAChart from Lazarus. After all, Maxima, Octave, nextnano, ... use gnuplot engine,
so its not a shame.
Carlos
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: Creating (scientific) plots via gnuplot

Post by srvaldez »

thank you badidea :-)
I also like Carlos Herrera's suggestion
<I apologize, badidea for spamming your thread, I deleted the offending part>
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Creating (scientific) plots via gnuplot

Post by badidea »

srvaldez wrote:thank you badidea :-)
I also like Carlos Herrera's suggestion
<I apologize, badidea for spamming your thread, I deleted the offending part>
I am not quickly offended, curious now what it was. This is also the first time for me trying gnuplot.
I will study your parallel topic (using gnuplot in Windows).
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Creating (scientific) plots via gnuplot

Post by dodicat »

You are allowed a little chuckle badidea, my windows 10 has finally packed in and I am now using Linux (Red hat)
I got hold of gnuplot from the repository (Yum), but I don't have cairo or png or z.

A little trick I use in Linux, create a folder called bin in your home folder (not root).
If you pop an executable into this bin folder it is automatically on path.
(You probably know this anyway as a Linux user)
I made up this code from bits and pieces using Gedit with language set at VB.NET, which highlights most fb syntax.
I have to compile from the terminal.
It is very basic, set the terminal command something like
plot "-5,8,sin(x)/x"
Which says plot sin(x) between -5 and 8.
(You may need ./plot "-5,8,sin(x)/x"), but if you put the executable in the above mentioned bin folder then plot seems to suffice.

Code: Select all

   
   
 
'plot.bas

 sub list()
    ? " abs()"
    ? " atn()"
    ? " cos()"
    ? " exp()"
    ? " fix()"
    ? " int()"
    ? " log()"
    ? " rnd()"
    ? " sgn()"
    ? " sin()"
    ? " sqr()"
    ? " tan()"
    ? " haversine()"
    ? " cosec()"
    ? " sec()"
    ? " cot()"
    ? " asin()"
    ? " acos()"
    ? " atn()"
    ? " arcsec()"
    ? " arccosec()"
    ? " arccot()"
    ? " sinh()"
    ? " cosh()"
    ? " tanh()"
    ? " sech()"
    ? " cosech()"
    ? " coth()"
    ? " arcsinh()"
    ? " arccoth()"
    ? " arctanh()"
    ? " arcsech()"
    ? " arccosech()"
    ? " pi()"
 end sub

#include "crt.bi"
Type screendata
    As Integer w,h,depth,bpp,pitch
    As Any Pointer row
    As Ulong Pointer pixel32
    As Ubyte Pointer pixel8
    As Ushort Pointer pixel16
End Type

Declare Sub automateY(As Double,As Double,Byref As Double,Byref As Double)
Declare Function eval(Byref sp As String ) As Double
Declare Function FindAndReplace(Byref instring As String,Byref ReplaceThis As String,Byref WithThis As String) As String
Declare Sub string_split(Byval s As String,chars As String,result() As String)

Dim Shared e_input    As String 
Dim Shared e_tok      As String 
Dim Shared e_spelling As String 
Dim Shared e_error    As Integer
Dim Pi As Double = 4 * Atn(1)
Dim As Double lower,upper
Dim As Double MinY,MaxY


'================================================================
Dim Shared As String fn


Function func(Byval x As Double) As Double
    static as string v,f
     v=str(x)
     if instr(v,"e")=0 then
     f=FindAndReplace(fn,"x",v)
     end if
    Return eval(f)   
End Function

Dim As String g=Command(1)
g=lcase(g)
Cls
if len(g)=0 then
    print
    print
    print "Plot Needs an instruction"
    print "type plot help at the command prompt"
    end
    end if
If g=Lcase("help") Then
    Print "plot ""start x value, finish x value, function (in x)"" "
    print "Everything must be inside quotes"
    Print "No spaces allowed, use a comma to seperate flags"
    print "operators ^ ( ) / * + - mod "
    Print "available functions :"
    list()
    Print "Example"
    Print "plot ""-3,3,sin(x)"" "
    End
End If

Redim As String a()

string_split(g,",",a())

If Ubound(a)<>3 Then Print "Input error":End
if eval(a(1))>=eval(a(2)) then Print "Input error on range":End


lower=eVal(a(1))           '<-------lower x range  here
upper=eVal(a(2))           '<-------upper x range  here
a(3)=Lcase(a(3))
Dim As String copy=a(3)
If Instr(a(3),"exp") Then
    a(3)= FindAndReplace(a(3),"exp","ezp")
End If
fn=a(3)

e_error=0
'Automate Y range by the sub automate
automateY(lower,upper,MinY,MaxY)
'=======================================================
'==============  PARSER START  ==================================
Function SEC(Byval x As Double) As Double
    SEC = 1 / Cos(x)
End Function

Function COSEC(Byval x As Double) As Double
    COSEC = 1 / Sin(x)
End Function

Function COT(Byval x As Double) As Double
    COT = 1 / Tan(x)
End Function

Function ARCSEC(Byval x As Double) As Double ''''''
    ARCSEC = Atn(x / Sqr(x * x - 1)) + Sgn((x) -1) * (2 * Atn(1))
End Function

Function ARCCOSEC(Byval x As Double) As Double
    ARCCOSEC = Atn(x / Sqr(x * x - 1)) + (Sgn(x) - 1) * (2 * Atn(1))
End Function

Function ARCCOT(Byval x As Double) As Double
    ARCCOT = Atn(x) + 2 * Atn(1)
End Function

Function sech(Byval x As Double) As Double
    sech = 2 / (Exp(x) + Exp(-x))
End Function

Function cosech(Byval x As Double) As Double
    cosech = 2 / (Exp(x) - Exp(-x))
End Function

Function coth(Byval x As Double) As Double
    coth = (Exp(x) + Exp(-x)) / (Exp(x) - Exp(-x))
End Function

Function arcsinh(Byval x As Double) As Double
    arcsinh = Log(x + Sqr(x * x + 1))
End Function

Function arccosh(Byval x As Double) As Double
    arccosh = Log(x + Sqr(x * x - 1))
End Function

Function arctanh(Byval x As Double) As Double
    arctanh = Log((1 + x) / (1 - x)) / 2
End Function

Function arcsech(Byval x As Double) As Double
    arcsech = Log((Sqr(-x * x + 1) + 1) / x)
End Function

Function arccosech(Byval x As Double) As Double
    arccosech = Log((Sgn(x) * Sqr(x * x + 1) +1) / x)
End Function

Function arccoth(Byval x As Double) As Double
    arccoth = Log((x + 1) / (x - 1)) / 2
End Function

Function HAVERSINE(Byval x As Double) As Double
    HAVERSINE = (Sin(x/2))^2
End Function

function pie(byval x as double=1) as double
    return (4*atn(1))*x
    end function

Function e_function(Byref fun As String,Byval arg As Double) As Double
    Dim n As Double
   
    Select Case Lcase(fun)
    Case "abs": n = Abs(arg)
    Case "atn": n = Atn(arg)
    Case "cos": n = Cos(arg)
    Case "exp": n = Exp(arg)
    Case "ezp": n = Exp(arg)
    Case "fix": n = Fix(arg)
    Case "int": n = Int(arg)
    Case "log": n = Log(arg)
    Case "rnd": n = Rnd(arg)
    Case "sgn": n = Sgn(arg)
    Case "sin": n = Sin(arg)
    Case "sqr": n = Sqr(arg)
    Case "tan": n = Tan(arg)
    Case "haversine":n=haversine(arg)
    Case "cosec":n=cosec(arg)
    Case "sec":n=sec(arg)
    Case "cot": n=cot(arg)
    Case "asin":n=Asin(arg)
    Case "acos":n=Acos(arg)
    Case "atn":n=Atn(arg)
    Case "arcsec":n=arcsec(arg)
    Case "arccosec":n=arccosec(arg)
    Case "arccot":n=arccot(arg)
    Case "sinh":n=sinh(arg)
    Case "cosh":n=cosh(arg)
    Case "tanh":n=tanh(arg)
    Case "sech":n=sech(arg)
    Case "cosech":n=cosech(arg)
    Case "coth":n=coth(arg)
    Case "arcsinh":n=arcsinh(arg)
    Case "arccoth":n=arccoth(arg)
    Case "arctanh":n=arctanh(arg)
    Case "arcsech":n=arcsech(arg)
    Case "arccosech":n=arccosech(arg)
    Case "pi"      :n=pie(arg)
    Case Else
        If Not e_error Then
            Locate 1,1
            Print "UNDEFINED FUNCTION " + fun
            Print
            e_error = -1
        End If
    End Select
    e_function = n
End Function

Sub e_nxt()
    Dim is_keyword As Integer
    Dim c As String
    e_tok = ""
    e_spelling = ""
    Do
        c = Left(e_input, 1)
        e_input = Mid(e_input, 2)
    Loop While c = " " Or c = Chr(9) Or c = Chr(13) Or c = Chr(10)
   
    Select Case Lcase(c)
   
    Case "0" To "9", "."
        e_tok = "num"
        Do
            e_spelling = e_spelling + c
            c = Left(e_input, 1)
            e_input = Mid(e_input, 2)
        Loop While (c >= "0" And c <= "9") Or c = "."
        e_input = c + e_input
       
    Case "a" To "z", "_"
        Dim As Integer is_id
        e_tok = "id"
        Do
            e_spelling = e_spelling + c
            c = Lcase(Left(e_input, 1))
            e_input = Mid(e_input, 2)
            is_id = (c >= "a" And c <= "z")
            is_id = is_id Or c = "_" Or (c >= "0" And c <= "9")
        Loop While is_id
        e_input = c + e_input
        is_keyword = -1
        Select Case Lcase(e_spelling)
        Case "and"
        Case "eqv"
        Case "imp"
        Case "mod"
        Case "not"
        Case "or"
        Case "xor"
        Case Else: is_keyword = 0
        End Select
        If is_keyword Then
            e_tok = Lcase(e_spelling)
        End If
       
    Case "<", ">"
        e_tok = c
        c = Left(e_input, 1)
        If c = "=" Or c = ">" Then
            e_tok = e_tok + c
            e_input = Mid(e_input, 2)
        End If
       
    Case Else
        e_tok = c
    End Select
   
    If e_spelling = "" Then
        e_spelling = e_tok
    End If
End Sub

Sub e_match (Byref token As String)
    If Not e_error And e_tok <> token Then
        Locate 1,1
        Print "EXPECTED " + token + ", got '" + e_spelling + "'"
        e_error = -1':end
    End If
    e_nxt()
End Sub

Function e_prs (Byval p As Integer) As Double
    Dim n   As Double 
    Dim fun As String 
    If e_tok = "num" Then
        n = Val(e_spelling)
        e_nxt()
    Elseif e_tok = "-" Then
        e_nxt()
        n = -e_prs(12)   ''   11 before 
    Elseif e_tok = "not" Then
        e_nxt()
        n = Not e_prs(6)
    Elseif e_tok = "(" Then
        e_nxt()
        n = e_prs(1)
        e_match(")")
    Elseif e_tok = "id" Then
        fun = e_spelling
        e_nxt()
        e_match("(")
        n = e_prs(1)
        e_match(")")
        n = e_function(fun, n)
    Else
        If Not e_error Then
            Locate 1,1
            Print "syntax error, at '" + e_spelling + "'"
            e_error = -1':end
        End If
    End If
   
    Do While Not e_error
        If     p <= 11 And e_tok = "^"   Then
            e_nxt(): n = n ^ e_prs(12)
        Elseif p <= 10 And e_tok = "*"   Then
            e_nxt(): n = n *   e_prs(11)
        Elseif p <= 10 And e_tok = "/"   Then
            e_nxt(): n = n /   e_prs(11)
        Elseif p <= 9  And e_tok = "\"   Then
            e_nxt(): n = n \   e_prs(10)
        Elseif p <= 8  And e_tok = "mod" Then
            e_nxt(): n = n Mod e_prs(9)
        Elseif p <= 7  And e_tok = "+"   Then
            e_nxt(): n = n +   e_prs(8)
        Elseif p <= 7  And e_tok = "-"   Then
            e_nxt(): n = n -   e_prs(8)
        Elseif p <= 6  And e_tok = "="   Then
            e_nxt(): n = n =   e_prs(7)
        Elseif p <= 6  And e_tok = "<"   Then
            e_nxt(): n = n <   e_prs(7)
        Elseif p <= 6  And e_tok = ">"   Then
            e_nxt(): n = n >   e_prs(7)
        Elseif p <= 6  And e_tok = "<>"  Then
            e_nxt(): n = n <>  e_prs(7)
        Elseif p <= 6  And e_tok = "<="  Then
            e_nxt(): n = n <=  e_prs(7)
        Elseif p <= 6  And e_tok = ">="  Then
            e_nxt(): n = n >=  e_prs(7)
        Elseif p <= 5  And e_tok = "and" Then
            e_nxt(): n = n And e_prs(6)
        Elseif p <= 4  And e_tok = "or"  Then
            e_nxt(): n = n Or  e_prs(5)
        Elseif p <= 3  And e_tok = "xor" Then
            e_nxt(): n = n Xor e_prs(4)
        Elseif p <= 2  And e_tok = "eqv" Then
            e_nxt(): n = n Eqv e_prs(3)
        Elseif p <= 1  And e_tok = "imp" Then
            e_nxt(): n = n Imp e_prs(2)
        Else
            Exit Do
        End If
    Loop
    e_prs = n
End Function

Function eval(Byref sp As String ) As Double
    Dim As Double value
    e_error = 0
    e_input = sp
    e_nxt()
    value = e_prs(1)
    If Not e_error Then Return value else e_error=0
   
End Function

Function FindAndReplace(Byref instring As String,Byref ReplaceThis As String,Byref WithThis As String) As String
    Var lens1=Len(ReplaceThis),lens2=Len(WithThis)
    If lens1=lens2 Then lens1=0
    Dim As String s=instring
    Dim As Integer position=Instr(s,ReplaceThis)
    While position>0
        If lens1 Then   
            s=Left(s,position-1) & WithThis & Mid(s,position+Lens1)
        Else
            Mid(s,position) = WithThis
        End If
        position=Instr(position+Lens2,s,ReplaceThis)
    Wend
    Function=s
End Function
'=================== END OF PARSER =======================
Sub string_split(Byval s As String,chars As String,result() As String)
    Redim result(0)
    Dim As String var1,var2
    Dim As Long pst
    #macro split(stri)
    pst=Instr(stri,chars)
    var1="":var2=""
    If pst<>0 Then
        var1=Mid(stri,1,pst-1)
        var2=Mid(stri,pst+1)
    Else
        var1=stri
    End If
    If Len(var1) Then
        Redim Preserve result(1 To Ubound(result)+1)
        result(Ubound(result))=var1
    End If
    #endmacro
    Do
        split(s):s=var2
    Loop Until var2=""
End Sub
'Bressenham line 2d
Sub bline(sd As screendata,x1 As Integer,y1 As Integer,x2 As Integer,y2 As Integer,col As Ulong)
    #define ppset32(_x,_y,colour) *Cptr(Ulong Ptr,sd.row+ (_y)*sd.pitch+ (_x) Shl 2)  =(colour)
    #define onscreen ((x1+x)>=0) And ((x1+x)<(sd.w-1)) And ((y1+y)>=0) And ((y1+y)<(sd.h-1))
    Var dx=Abs(x2-x1),dy=Abs(y2-y1),sx=Sgn(x2-x1),sy=Sgn(y2-y1)
    Dim As Integer e
    If dx<dy Then  e=dx\2 Else e=dy\2
    Do
        For x As Integer=0 To 1
            For y As Integer=0 To 1
                If onscreen Then
                     ppset32((x1+x),(y1+y),col)
                End If
            Next y
        Next x
        If x1 = x2 Then If y1 = y2 Then Exit Do
        If dx > dy Then
            x1 += sx : e -= dy : If e < 0 Then e += dx : y1 += sy
        Else
            y1 += sy : e -= dx : If e < 0 Then e += dy : x1 += sx
        End If
    Loop
End Sub
'Bressenham line thickened
Sub thickline(sd As screendata,_
    x1 As Long,_
    y1 As Long,_
    x2 As Long,_
    y2 As Long,_
    thickness As Single,_
    colour As Ulong)
    Var h=Sqr((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
    Var s=(y1-y2)/h
    Var c=(x2-x1)/h
    For yp As Double=-thickness/2 To thickness/2 'step 2
        bline(sd,x1+(s*yp),y1+(c*yp),x2+(s*yp),y2+(c*yp),colour)
    Next yp
End Sub

Sub automateY(Lx As Double,Ux As Double,Byref  ly As Double,Byref uy As Double)
    ly=1e10:uy=-1e10
    For n As Double=lx To ux Step(ux-lx)/100
        Dim As Double f=func(n)
        If ly>f Then ly=f
        If uy<f Then uy=f
    Next n
End Sub
Dim As Integer xres,yres
Dim As Double PLOT_grade=500
dim as double lx,ly
#macro sketch(_function,minx,maxx,miny,maxy,col)
locate 3,3
print a(3)
For x As Double=minx To maxx Step (maxx-minx)/PLOT_GRADE
    Dim As Double x1=(xres)*(x-minx)/(maxx-minx)
    Dim As Double y1=(yres)*(_function-maxy)/(miny-maxy)
    If x=minx Then lx=x1:ly=y1'Pset(x1,y1),col Else Line -(x1,y1),col
thickline(thisscreen,lx,ly,x1,y1,2,col)
lx=x1:ly=y1
Next x
#endmacro

#macro _axis(colour)
Scope
    Dim As Long flagx,flagy
    If Sgn(miny)<>Sgn(maxy) Then
        flagx=1
        Line(0,(yres-(miny/(miny-maxy))*yres))-(xres,(yres-(miny/(miny-maxy))*yres)),colour 'x axis
        Endif
        If Sgn(minx)<>Sgn(maxx) Then
            flagy=1
            Line(((minx/(minx-maxx))*xres),0)-(((minx/(minx-maxx))*xres),yres),colour 'y axis
        End If
        If flagx=1 Then
            Draw String(0,(yres-(miny/(miny-maxy))*yres)),Str(minx),colour
            Draw String(xres-8-8*(Len(Str(maxx))),(yres-(miny/(miny-maxy))*yres)),Str(maxx),colour
        Else
            Draw String(0,yres/2),Str(minx),colour
            Draw String(xres-8-8*(Len(Str(maxx))),yres/2),Str(maxx),colour
        End If
        If flagy=1 Then
            Draw String(((minx/(minx-maxx))*xres),0),Str(maxy),colour
            Draw String(((minx/(minx-maxx))*xres),yres-16),Str(miny),colour
        Else
            Draw String(xres/2,0),Str(maxy),colour
            Draw String(xres/2,yres-16),Str(miny),colour
        End If
    End Scope
    #endmacro


    Screen 19,32,,&h20
color ,rgb(0,100,200)
 Dim As screendata ThisScreen
    With ThisScreen
        Screeninfo .w,.h,.depth,.bpp,.pitch
        .row=Screenptr
    End With

    Screeninfo xres,yres
        Cls

        sketch(func(x),lower,upper,MinY,Maxy,Rgb(0,200,0))
       
        Var minx=lower,maxx=upper
        _axis(Rgb(200,200,200))
       
        Sleep

      
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Creating (scientific) plots via gnuplot

Post by badidea »

dodicat wrote:You are allowed a little chuckle badidea, my windows 10 has finally packed in and I am now using Linux (Red hat)
Was it you who said "I am too old to linux"?
dodicat wrote:I got hold of gnuplot from the repository (Yum), but I don't have cairo or png or z.
I have never used Red hat, but I would expect libpng and zlib/libz in the repository.
dodicat wrote:A little trick I use in Linux, create a folder called bin in your home folder (not root).
If you pop an executable into this bin folder it is automatically on path.
(You probably know this anyway as a Linux user)
I did not. One lifetime is not enough to learn everything that is possible with linux (or windows).
Programs installed via the repository are automatically in the right path it seems. And for my own programs I just use ./program or a double click from the graphical file manager. In the graphical file manager, I can also choose 'open terminal here'.
dodicat wrote:I made up this code from bits and pieces using Gedit with language set at VB.NET, which highlights most fb syntax.
I have to compile from the terminal.
I use Geany (also on windows) which also does syntax highligting and allows key bindings for compile & run.
dodicat wrote: It is very basic, set the terminal command something like
plot "-5,8,sin(x)/x"
Which says plot sin(x) between -5 and 8.
(You may need ./plot "-5,8,sin(x)/x"), but if you put the executable in the above mentioned bin folder then plot seems to suffice.
Nice tool, but gnuplot looks better with more options, like log scale, grid, second y-axis etc.
Also, I don't want to plot sin(x)/x or some other function, but actual data from a spreadsheet / database and do some data fitting.
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Creating (scientific) plots via gnuplot

Post by TJF »

jj2007 wrote:Challenge? ;-)
Challenge? ;-)
aurelVZAB
Posts: 666
Joined: Jul 02, 2008 14:55
Contact:

Re: Creating (scientific) plots via gnuplot

Post by aurelVZAB »

I don't know how i miss this topic.
I am looking for a way to plot electric current density on metal surfacees
anyone know thee way how to do that..
i found something on python but is not exactly what i want ..
anyone?
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Creating (scientific) plots via gnuplot

Post by jj2007 »

@Aurel: Do you mean software or hardware, i.e. plot on a metal surface???
TJF wrote:
jj2007 wrote:Challenge? ;-)
Challenge? ;-)
Absolutely! Just post the code that uses badidea's data and displays it as close as possible to his version ;-)
Here is my version (complete source; the window is sizeable, ArrayPlotValues is optional):

Code: Select all

include \masm32\MasmBasic\Res\MbGui.asm
  ArraySet current() As double=120, 125, 122, 119, 134, 138, 141, 159, 162, 178
  ArraySet voltage() As double=250, 210, 180, 155, 140, 125, 110, 100, 90, 80
  SetAxisX "Current", s 0, d 1/5, grid 1   ; s=start value, d=difference between gridlines
  SetAxisY "Voltage", s 0, d 5/5, grid 1
Event Paint
  ArrayPlotValues "%i V", 8, -12        ; format$, dx & dy positions
  ArrayPlot current():voltage(), RgbCol(0, 0, 255), lines=3
  ArrayPlot exit, "Voltage over Current"
EndOfCode
Image
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Creating (scientific) plots via gnuplot

Post by TJF »

@jj2007: It's a joke ;-)
jj2007 wrote:Absolutely! Just post the code that uses badidea's data and displays it as close as possible to his version ;-)
Your output has numbers with decimal point, but no digits. The X scaling has fancy numbers, and the whole thing doesn't match DIN 461. Do you really want to compete, by which criterias?

We should stop poluting the thread from badidea.
aurelVZAB wrote:I am looking for a way to plot electric current density on metal surfacees
Colored 3D surface plots were on my ToDo list, but sorry, I ran out of time for this project. 30 year ago I bought a graphic program called X-Act for this task, and I'm still using it, if need be. Meanwhile there's an obviously free text based alternative from this company.

Regards
aurelVZAB
Posts: 666
Joined: Jul 02, 2008 14:55
Contact:

Re: Creating (scientific) plots via gnuplot

Post by aurelVZAB »

TJF:
Colored 3D surface plots were on my ToDo list

It is not 3D than 2D colored ploting.
I already ask(talk with) jj on another forum.
So it is about scientific plots..yes is not with gnuplot.
I think that should be created with any drawing functions if we know proper formulas.
I don't know how complex might be ..

result should be like this :

Image
aurelVZAB
Posts: 666
Joined: Jul 02, 2008 14:55
Contact:

Re: Creating (scientific) plots via gnuplot

Post by aurelVZAB »

or like this:
Image
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Creating (scientific) plots via gnuplot

Post by badidea »

TJF wrote:...and the whole thing doesn't match DIN 461
I did not know that the Germans even standardized data plots :-)
TJF wrote:We should stop polluting the thread from badidea
No problem for me. Always good to know alternatives. But I don't want to spend to much time fiddling with graph layout details, so that is why I continue with gnuplot for now. The integration with FreeBASIC is a bit minimal, but in the end I only need the generated PNG-files.

Currently working on some data-filtering before I continue making fancy plots. I started in python with matplotlib, but I still dislike python.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Creating (scientific) plots via gnuplot

Post by dodicat »

Here is dislin for 32 bit win.
https://www.mediafire.com/file/ptg394tg ... n.zip/file
I have popped in the required .dll and and the .bi calling it.(although dislin.bi is in the fb distribution anyway, but asking for a different .dll). Test the examples in the folder ,
Dislin is an ongoing project.
No need for extensive installations of Cairo e.t.c., just what is in the folder will do.
If you don't like it just send it to the bin.
Also the fawlty programming language has nice plotting capabilities.
...
(Fawlty Language 0.79.46 (windows amd64 m64) Copyright (c) 2000-2019, Lajos Foldy).
But that is another story.
Post Reply