Plot mathematical formula: alternatives to XojoScript


I am using a simple xojoscript (as below) to evaluate mathematical formulas. I basically use this to produce plots of user-defined mathematical formulas. Everything works, but I have 3 big problems:

  • I have to generate the x-coordinate of each point to evaluate (the xojoscript returns the corresponding y-coordinate)
  • It is extremely slow
  • I have to figure out how many points I need for a smooth result and how they should be spaced

The last point is particularly annoying as the number of points to generate in a DeltaX range should be related to the derivative of the function in that range…

So, I wonder if there are better ways…


[code] Dim source As String

source = "Dim expr As Variant = " + expression + EndOfLine + _
"If expr.IsNumeric Then " + EndOfLine + _
“mResult = Str(expr.DoubleValue)” + EndOfLine + _
"Else " + EndOfLine + _
“mResult = expr” +EndOfLine + _
“End If”

Self.Source = source
Self.Context = Self // So we can refer to Evaulator.mResult property in XojoScript
Return mResult[/code]

you could pass arrays of values rather than 1 value at a time

I do this to plot z=f(x,y) where the user codes f() in XojoScript and its fast. I think the problem is you’re recompiling for every point to compute. Instead I add boiler plate head and tail code to the user supplied source so that the input values are calculated in the script and then saved out to the context. All the sampling of the function happens in a single run of the script.

So a user script like

y = x * x

becomes something like

dim y As double dim x As double dim i As integer for i = 0 to 200 x = i / 100 - 1 //========= user code y = x * x //========= setCoord(i, y) next

The context has a method for collecting the returned values and storing them in an array. When the script is finished that array is used for drawing.

For what it’s worth here’s the code I have, maybe you can make sense of it. sourceTA is the TextArea with user code, setCoord stores the values in the 2D array matField. display is an OpenGLSurface and dlField is a Display List object of the created model/drawing.

[code]Private Sub buildField()

script.Context = self

//============================ combine script source code
static srcHead As String
static srcTail As String
if srcHead = “” then
dim sa() As String
sa.Append “dim ix As integer”
sa.Append “dim iy As integer”
sa.Append “dim x As double”
sa.Append “dim y As double”
sa.Append “dim z As double”
sa.Append “for iy = 0 to 200”
sa.Append “for ix = 0 to 200”
sa.Append “y = iy / 100.0 - 1.0”
sa.Append “x = ix / 100.0 - 1.0”
sa.Append “”
srcHead = Join(sa, EndOfLine)
redim sa(-1)
sa.Append “”
sa.Append “setCoord(ix, iy, z)” //context
sa.Append “next”
sa.Append “next”
srcTail = Join(sa, EndOfLine)

script.Source = srcHead + sourceTA.Text.Trim + srcTail

//======================================= skip out if didn’t compile
redim scriptErrs(-1)
if script.Precompile(XojoScript.OptimizationLevels.High) then
errLabel.Text = “”
errLabel.Text = Join(scriptErrs)

//======================================= run script and fill all matField values
redim matField(200, 200)

//======================================= build the ‘drawing’ from matField
dlField = new EZDisplayList

dim g As ezgl = display.getEZGL
dim x, y, xp1 As integer, tx0, tx1, ty As single
for x = 0 to 199
xp1 = x + 1
tx0 = x / 100
tx1 = xp1 / 100
for y = 0 to 200
ty = -y / 100’200
g.draw.textureCoord(tx0, ty)
g.draw.vertex(tx0, matField(x, y), ty)
g.draw.textureCoord(tx1, ty)
g.draw.vertex(tx1, matField(xp1, y), ty)



End Sub

For this you will need to track the slope and subsample if it’s too steep. And you’ll need to store the input sampling value along with the result as sampling isn’t linear. This is a numerical approach and fails in situations like sampling Sin(x) every 2pi, slope is always 0 so no subsampling occurs. The only way I know to not fail like that is to have the actual derivative expression.

Ah I was wondering how that was done… Sounds like it is not simple and there is solution for all cases… yet looking at at the pro plotting packages, the curves look good no matter what you grow a them,so there must be a general solution.

Thank you Will for the code. I will take a look at it.