Macro (SVB) Programs Example - Creating User-Defined Custom Functions

User-defined custom functions
Via Statistica Visual Basic, a user-defined fit can be created and used for 2D and 3D XYZ graphs. This functionality can be used for both fits and custom functions. It is enabled when using event macros and is useful for creating highly domain-specific types of graphs (e.g., special types of wafer plots used in the semiconductor industry).
Example program
This program illustrates how to create a user-defined custom function. When the macro is run, a 3D scatterplot is created using variables from the active data set, and then a user-defined fit (SuperFit) is applied to the data. As long as the macro continues to run, this “SuperFit” will be available on the Plot: Fitting tab of the Graph Options dialog box.

Option Base 1

Option Explicit

Dim WithEvents Newgraph As Graph

Sub Main

'Create a graph object naming it "3D Scatterplot with Circular Wafer Fit"

Set Newgraph = Graphs.New("3D Scatterplot with Circular Wafer Fit")

'Create the graph content, in this case 3D scatterplot

Dim Newlayout As Layout3DScatterplot

Set Newlayout = Newgraph.GraphObject.CreateContent(scg3DScatterplot)

'This statement retrieves the number of cases

'in the current "active for input" data Spreadsheet.

Dim NCases As Long

NCases = ActiveDataSet.NumberOfCases

'Add a plot to the graph

Dim Newplot As Plot3DScatterplot

Set Newplot = Newlayout.Plots.Add(NCases)

'This section enables the user To select the variables from the active data set

'First, declare the arrays of Integer's or Long's, to hold the variable lists;

'Note that they are declared here without an Explicit dimension yet.

Dim VarList1 () As Long

Dim VarList2 () As Long

Dim VarList3 () As Long

Dim Nvars As Long

'The InList1, 2, and 3 will hold the number of variables that were selected.

Dim InList1 As Long

Dim InList2 As Long

Dim InList3 As Long

Dim ret As Integer

'This statement retrieves the number of variables

'in the current "active for input" data Spreadsheet.

Nvars = ActiveDataSet.NumberOfVariables

If Nvars<1 Then GoTo Finish

'Next the array of Long values VarList1, 2, and 3 are re-dimensioned;

'We will allow up To the total number of Variables In the Input Spreadsheet

'To be selected into the VarLists.

ReDim VarList1(1 To Nvars)

ReDim VarList2(1 To Nvars)

ReDim VarList3(1 To Nvars)

'Note that SelectVariables functions return 1 if the user exited by pressing OK;

'They return 0 if the user exited by pressing Cancel.

ret = SelectVariables3 (ActiveDataSet, _

"Variables for Analysis", _

1, Nvars, VarList1, InList1, "X-axis variable", _

1, Nvars, VarList2, InList2, "Y-axis variable", _

1, Nvars, VarList3, InList3, "Z-axis variable")

Dim xVar As Integer, yVar As Integer, zVar As Integer

xVar = VarList1(1)

yVar = VarList2(1)

zVar = VarList3(1)

If ret=0 Then GoTo Finish

Dim i As Integer

For i = 1 To NCases

Newplot.Variable(1).Value(i) = ActiveDataSet.Value(i, xVar)

Newplot.Variable(2).Value(i) = ActiveDataSet.Value(i, yVar)

Newplot.Variable(3).Value(i) = ActiveDataSet.Value(i, zVar)

Next

Finish:

'Add a fit to the plot

Dim Newfit As Fit3D

Set Newfit = Newplot.Fits.Add()

'Set the fit type to user defined. This is what tells the graph to call our custom fit event.

Newfit.FitType = scgFitUserDefined

'This property allows you to make the graph round.

Newlayout.Circular = True

'Here you can control the number of grid lines displayed on the fit

Dim s As Surface

Set s = Newfit.Surface

s.DisplayLinesX = 50

s.DisplayLinesY = 50

'Add titles

Newgraph.Titles.Add(scgMainTitle, "Custom fit from spreadsheet " + ActiveDataSet.Name)

Newlayout.Axes.XAxis.Title.Text = ActiveDataSet.VariableName(xVar)

Newlayout.Axes.YAxis.Title.Text = ActiveDataSet.VariableName(yVar)

Newlayout.Axes.ZAxis.Title.Text = ActiveDataSet.VariableName(zVar)

'Make the graph visible

Newgraph.Visible = True

'Since the macro needs to continue running for it to be able to respond to events,

'we create an infinite Loop

Do

DoEvents

Loop

End Sub

Private Sub Newgraph_OnCalculateFit3D(ByVal Mode As StatisticaGraphics.UserFitMode, ByVal PlotID As Long, ByVal FitID As Long, ByVal xStart As Double, ByVal xStep As Double, ByVal xCut As Long, ByVal yStart As Double, ByVal yStep As Double, ByVal yCut As Long, ByVal dataCount As Long, xValues() As Double, yValues() As Double, zValues() As Double, status As Long, FitValues() As Double, FitName As String)

'Mode will either equal UserFitModeTest or UserFitModeCalculate.

'In Test Mode it Is going To retrieve the Name of the Fit As well As a status value.

'A status value of 2 tells the event that when it fires in UserFitModeCalculate mode,

'it will return the Graph data In the arrays xValues(), yValues(), And zValues().

'You could use those arrays to base your calculations based on the data in the graph.

'If you don't need the data you can set the status = 1 which will

'generate less overhead when firing the event.

If Mode = UserFitModeTest Then

FitName = "SuperFit"

'status set to 1 tells the event to not include the raw data

'when the Event Is fired In UserFitModeCalculate Mode.

'If the status is set to 2 it will include the raw data.

status = 2

Else 'UserFitModeCalculate

'This is where the Fit data is returned to the graph.

'FitValues() Is a one dimensional Array that should be Set To the Size xCut * yCut.

Const MISSING_VAL = -999999998

ReDim FitValues(1 To (xCut * yCut))

Dim i As Integer

'Initialize FitValues elements to MISSING_VAL

For i = LBound(FitValues) To UBound(FitValues)

FitValues(i) = MISSING_VAL

Next

'Find the min and max values for the X and Y coordinates used in the graph

Dim dMinX As Double, dMaxX As Double

Dim dMinY As Double, dMaxY As Double

dMinX = dMaxX = xValues(LBound(xValues))

For i = LBound(xValues) + 1 To UBound(xValues)

If xValues(i) > dMaxX Then dMaxX = xValues(i)

If xValues(i) < dMinX Then dMinX = xValues(i)

Next

dMinY = dMaxY = yValues(LBound(yValues))

For i = LBound(yValues) + 1 To UBound(yValues)

If yValues(i) > dMaxY Then dMaxY = yValues(i)

If yValues(i) < dMinY Then dMinY = yValues(i)

Next

'Get the ratio for number of cuts compared to x and y coordinate range

Dim ratioX As Double

ratioX = xCut/(dMaxX - dMinX)

Dim ratioY As Double

ratioY = yCut/(dMaxY - dMinY)

For i = LBound(xValues) To UBound(xValues)

Dim x As Double

Dim Y As Double

Dim z As Double

x = xValues(i)

Y = yValues(i)

z = zValues(i)

Dim xElement As Integer

Dim yElement As Integer

Dim element As Integer

xElement = (xValues(i) - dMinX) * ratioX

yElement = (yValues(i) - dMinY) * ratioY

element = (xElement - 1) * yCut + yElement

If (element < LBound(FitValues)) Then element = LBound(FitValues)

If (element > UBound(FitValues)) Then element = UBound(FitValues)

FitValues(element) = z

Next

Dim lastVal As Double

'Find the first value and set lastVal equal to it

For i = LBound(FitValues) To UBound(FitValues)

If FitValues(i) <> MISSING_VAL Then

lastVal = FitValues(i)

Exit For

End If

Next

'Make sure there aren't any blank values

For i = LBound(FitValues) To UBound(FitValues)

If FitValues(i) = MISSING_VAL Then

FitValues(i) = lastVal

Else

lastVal = FitValues(i)

End If

Next

'status set to 1 indicates success

status = 1

End If

End Sub

Macro results
Once the user has selected the variables to use from the active spreadsheet, a graph is created with the user-defined fit defined in the macro. In this example, the macro was run using the example data file Exp.sta.

As long as the macro is running, SuperFit can be selected in the Fit type drop-down list on the Plot: Fitting tab of the Graph Options dialog box for this graph.