This is InspectPFunc.m in view mode; [Download] [Up]
/* InspectPFunc.h by Paul Kunz November 1992
* HippoDraw inspector for Minuit fitting
*
* Copyright (C) 1992 The Board of Trustees of
* The Leland Stanford Junior University. All Rights Reserved.
*/
#import "Draw.subproj/draw.h"
#import "InspectPFunc.h"
const char InspectPFunc_h_rcsid[] = INSPECTPFUN_H_ID;
const char InspectPFunc_m_rcsid[] = "$Id: InspectPFunc.m,v 1.71.2.5 1994/02/08 20:29:36 rensing Exp $";
#import "FineSlider.h"
#import "HDrawApp.h"
#import "HGraphicView.h"
#import "NewInspector.h"
#import "Plot.h"
#import "PFunction.h"
#import "LinearX.h"
#import "LinearY.h"
#import "Gaussian.h"
#import "Quadratic.h"
#import "Exponential.h"
#import "Zshape.h"
#import "PowerLaw.h"
#import "Minuit.h"
#import <sys/dir.h>
#import <float.h>
#define FUNC_ALL 0
#define FUNC_PLOT 1
#define VALUE_TAG 0
#define LIMITS_TAG 1
#define PARM_MAX 0
#define PARM_VALUE 1
#define PARM_MIN 2
#define PARM_STEP 3
#define CHISQ_POS 0
#define DOF_POS 1
#define PLOT_INDIVIDUALS 0
#define PLOT_SUM 1
#define GPPCOMPILE
//#define GPPLIBRARY "/usr/local/lib/libg++.a"
#define GPPLIBRARY ""
#define GPPINCLUDEDIR "/usr/local/lib/g++-include"
#define PANEL_NAME "Plot Functions"
@interface InspectPFunc(Private)
- (NXBundle *) compile:(const char *)directory :(const char *)file;
/*
* Compiles, links, source file into a bundle and returns the intialized
* bundle
*/
- loadFunction:(const char *)directory :(const char *)file;
/*
* Loads the PFunction in directory/file and adds it to the
* available function list and to the dynamically loaded function
* list.
*/
- getFitData;
/*
* Get the data needed for a fit out of the display
*/
- loadMinuit;
/*
* Load Minuit with the current list of parameters
*/
- enableButtons:(BOOL) flag;
/*
* Enables or disables al buttons according to value of flag.
*/
- updateChiSq: (float) chi2;
/*
* Update chi^2 form with given value.
*/
- updateDoF;
/*
* update the degrees of freedom form
*/
- doSingleFit:sender;
/* fit a single plot */
@end
static id fitObject = nil;
static NXRect stopButtonFrame;
static double old_fcn_value;
static char *emptyBinTitle[] =
{"ignore","set error to 1","disable fitting"};
static char *doFitTitle[] =
{"unbinned","integral/fixed norm","integral over bin",
"average over bin","1 sample per bin"};
static char *lineStyleTitle[] =
{"Solid","Dash","Dot","DotDash","Invisible"};
@implementation InspectPFunc
/* Function called by Minuit FCN */
void refreshdisplay_(double *fcn_value, int *iflag, int *quit_minuit)
{
NXEvent *event;
NXPoint where;
float cutoff = 0.025;
double delta;
if ( *iflag >= 2 ) {
delta = (*fcn_value - old_fcn_value) / old_fcn_value;
if (delta < -cutoff) {
old_fcn_value = MAX(fabs(*fcn_value), 1e-06);
[fitObject updateParmForm];
[fitObject updateChiSq: *fcn_value];
[fitObject updatePlot];
}
}
*quit_minuit = 0;
if ((event = [NXApp peekAndGetNextEvent:NX_MOUSEDOWNMASK]) != NULL) {
where = event->location;
if (NXPointInRect(&where, &stopButtonFrame)) {
if (NXRunAlertPanel("Cancel fitting warning",
"Do you really want to cancel this fit ?",
"OK", "continue fitting", NULL)
== NX_ALERTDEFAULT) {
*quit_minuit = 1;
}
}
}
return;
}
void loadParams(double *parm)
{
[fitObject loadParams:parm];
}
- initInspFor:aDraw
{
NXBundle *bundle;
PopUpList *popup;
PFunction *func;
char buf[MAXPATHLEN+1];
int itype;
[super initInspFor:aDraw];
/* Initial Function List */
availFuncList = [[List allocFromZone:[self zone]] initCount:0];
dynamFuncList = [[List allocFromZone:[self zone]] initCount:0];
func = [[Gaussian allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[LinearX allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[LinearY allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[Quadratic allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[Exponential allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[PowerLaw allocFromZone:[self zone]] init];
[availFuncList addObject:func];
func = [[Zshape allocFromZone:[self zone]] init];
[availFuncList addObject:func];
/* Load interface */
bundle = [NXBundle bundleForClass:[self class]];
if ( [bundle getPath:buf forResource:"InspectPFunc" ofType:"nib"] ) {
[NXApp loadNibFile:buf owner:self
withNames:NO fromZone:[self zone]];
}
[funcBrowser setMinColumnWidth:1];
[funcBrowser setMaxVisibleColumns:2];
[funcBrowser setTitle:"Functions" ofColumn:0];
[funcBrowser loadColumnZero];
[funcBrowser addColumn];
[self updateBrowser];
[funcRadio sendAction];
[theInspector addView:[contentBox contentView]
withName:"Plot Functions" withSupervisor:self];
[theInspector showPanelWithName:PANEL_NAME];
popup = [emptyBinButton target];
emptyBinMatrix = [popup itemList];
[popup setTarget:self];
[popup setAction:@selector(ebTypeChanged:)];
popup = [fitTypeButton target];
fitTypeMatrix = [popup itemList];
[popup setTarget:self];
[popup setAction:@selector(fitTypeChanged:)];
itype = FIT_ONESAMPLE;
[fitTypeMatrix selectCellWithTag:itype];
[fitTypeButton setTitle:doFitTitle[itype]];
popup = [plotFuncButton target];
plotFuncMatrix = [popup itemList];
[popup setTarget:self];
[popup setAction:@selector(plotFuncLineStyleChanged:)];
[plotFuncMatrix selectCellWithTag:0];
[plotFuncButton setTitle:lineStyleTitle[0]];
popup = [plotFSumButton target];
plotFSumMatrix = [popup itemList];
[popup setTarget:self];
[popup setAction:@selector(plotFSumLineStyleChanged:)];
[plotFSumMatrix selectCellWithTag:0];
[plotFSumButton setTitle:lineStyleTitle[0]];
fitObject = self;
return self;
}
- setFitter:anObject
{
fitter = anObject;
return self;
}
/* Action Methods */
- add:sender
{
List *list;
parm_t ptype;
id matrix;
display disp;
graphtype_t type;
id func, newFunc;
int i, irc;
if ( !selectedPlot ) return self;
ptype = [parmRadio selectedRow];
if ( ptype != PLAY_VALUES ) {
list = [selectedPlot functionList];
[list makeObjectsPerform:@selector(copyToVaried:) with:(id)&ptype];
[parmRadio selectCellAt:PLAY_VALUES :0];
ptype = PLAY_VALUES;
}
matrix = [funcBrowser matrixInColumn:0];
i = [matrix selectedRow];
if ( i < 0 ) {
return self;
}
disp = [selectedPlot histDisplay];
type = h_getDispType( disp );
if ( (type == XYPLOT || type == STRIPCHART ) &&
h_getBinding(disp,YERROR) < 0 ) {
irc = NXRunAlertPanel( "Warning",
"Chi-Square and errors from fitting will have no"
" meaning without y-errors assigned to plot.",
"Proceed", "Cancel", NULL );
if ( irc != NX_ALERTDEFAULT ) return self;
}
func = [availFuncList objectAt:i];
newFunc = [func copyFromZone:[selectedPlot zone]];
[selectedPlot addFunction:newFunc];
list = [selectedPlot functionList];
[newFunc setInitialValues];
i = [[fitTypeMatrix selectedCell] tag];
[newFunc setFitType:&i];
[[funcRadio selectCellAt:0 :FUNC_PLOT] sendAction];
i = [plotFuncMatrix selectedRow];
[newFunc setFuncLineStyle:&i];
[list makeObjectsPerform:@selector(updateFromType:)with:(id)&ptype];
[self updatePlotMatrix];
[self updatePlot];
[self updateChiSq];
[[[graphicView window] delegate] dirty:self];
return self;
}
- remove:sender
{
if (!selectedPlot) return self;
if (!plotFunction) return self;
[selectedPlot removeFunction:plotFunction];
[plotFunction free];
lastPlot = nil;
[self updatePlotMatrix];
[self updatePlot];
[self updateView];
[self updateChiSq];
[[[graphicView window] delegate] dirty:self];
return self;
}
- browserClicked:sender
{
List *funcList;
Matrix *matrix;
int i;
if ( [funcRadio selectedCol] == FUNC_ALL ) {
[self enableButtons:NO];
return self;
}
if ( !selectedPlot ) {
[self enableButtons:NO];
return self;
}
funcList = [selectedPlot functionList];
matrix = [funcBrowser matrixInColumn:0];
i = [matrix selectedRow];
if ( (i < 0) && [matrix cellCount] ) {
i = 0; /* on coming forward, it's not yet set */
}
if ( i < 0 ) {
plotFunction = nil;
[self enableButtons:NO];
return self;
}
plotFunction = [funcList objectAt:i];
matrix = [funcBrowser matrixInColumn:1];
i = [matrix selectedRow];
if ( (i < 0) && [matrix cellCount] ) {
i = 0; /* on coming forward, it's not yet set */
}
if ( i < 0 ) {
[self enableButtons:NO];
return self;
}
pfIndex = i;
[self enableButtons:YES];
[self updateParmForm];
[self updatePlotMatrix];
return self;
}
- animateLimits:sender
{
Storage *parmList;
fitParm *parm;
parm_t type;
double amplitude, initialAngle, angle;
double initialValue;
float twoPi;
double min_value, mean_value, max_value;
int steps = 30;
type = [parmRadio selectedRow];
parmList = [plotFunction parmListOfType:type];
parm = [parmList elementAt:pfIndex];
initialValue = parm->value;
min_value = [ parmSlider mminValue ];
max_value = [ parmSlider mmaxValue ];
mean_value = 0.5*(max_value + min_value);
if (max_value == min_value) return self;
amplitude = 0.5*(max_value - min_value);
initialAngle = asin( (initialValue-mean_value)/amplitude );
twoPi = 2.0 * M_PI;
for (angle = 0.; angle <= twoPi; angle += twoPi / steps) {
parm->value = mean_value + amplitude * sin(initialAngle + angle);
[parmSlider setFloatValue:parm->value];
[plotFunction updateFromType:&type];
[self updatePlot];
[self updateChiSq];
}
parm->value = initialValue;
[plotFunction updateFromType:&type];
[parmSlider setFloatValue:initialValue ];
return self;
}
- sliderDragged:sender
{
List *list;
parm_t type;
float value;
type = [parmRadio selectedRow];
if ( type != PLAY_VALUES ) {
list = [selectedPlot functionList];
[list makeObjectsPerform:@selector(copyToVaried:) with:(id)&type];
[[parmRadio selectCellAt:PLAY_VALUES :0] sendAction];
}
value = [parmSlider floatValue];
[plotFunction setFloatValue:value at:pfIndex];
[parmForm setFloatValue:value at:PARM_VALUE];
[self updatePlot];
[self updateChiSq];
return self;
}
- sliderNudged:sender
{
Storage *parmList;
fitParm *parm;
List *list;
parm_t type;
float value;
int nudge;
type = [parmRadio selectedRow];
if ( type != PLAY_VALUES ) {
list = [selectedPlot functionList];
[list makeObjectsPerform:@selector(copyToVaried:) with:(id)&type];
[[parmRadio selectCellAt:PLAY_VALUES :0] sendAction];
}
parmList = [plotFunction parmListOfType:type];
parm = [parmList elementAt:pfIndex];
nudge= [sender tag];
if ( nudge < 0 ) {
value = parm->value - parm->step_size;
}
else {
value = parm->value + parm->step_size;
}
[plotFunction setFloatValue:value at:pfIndex];
[parmForm setFloatValue:value at:PARM_VALUE];
[self updatePlot];
[self updateChiSq];
return self;
}
- checkFitValid
{
List *list;
parm_t type;
type = [parmRadio selectedRow];
if ( type == FIT_VALUES && ![selectedPlot isFitted] ) {
list = [selectedPlot functionList];
[list makeObjectsPerform:@selector(copyToVaried:) with:(id)&type];
[self setParmType:PLAY_VALUES];
}
if ( ![selectedPlot isFitted] ) {
[[parmRadio cellAt:FIT_VALUES :0] setEnabled:NO ];
}
return self;
}
- textDidEnd:sender endChar:(unsigned short)whyEnd
{
float lower, upper, value, step;
parm_t type = [parmRadio selectedRow];
if (selectedPlot == NULL) return self;
upper = [parmForm floatValueAt:PARM_MAX];
value = [parmForm floatValueAt:PARM_VALUE];
lower = [parmForm floatValueAt:PARM_MIN];
step = [parmForm floatValueAt:PARM_STEP];
if ( [parmRadio selectedRow] != PLAY_VALUES ) {
[[selectedPlot functionList]
makeObjectsPerform:@selector(copyToVaried:) with:(id)&type];
[[parmRadio selectCellAt:PLAY_VALUES :0] sendAction];
}
switch ([parmForm selectedRow]) {
case PARM_MIN :
if ( lower > upper ) {
upper = lower + (upper - value);
}
if ( lower > value ) {
value = lower;
}
step = (upper - lower)/ 100.0;
break;
case PARM_MAX:
if ( upper < lower ) {
lower = upper - (value - lower);
}
if ( upper < value ) {
value = upper;
}
step = (upper - lower)/ 100.0;
break;
case PARM_VALUE:
if ( value < lower ) {
lower = value - (upper-value);
}
if ( value > upper ) {
upper = value + (value-lower);
}
step = (upper - lower)/ 100.0;
break;
case PARM_STEP:
if ( upper < (lower + 100*step) ) {
upper = lower + 100*step;
}
if ( lower > (upper + 100*step) ) {
lower = upper - 100*step;
}
break;
}
[parmForm setFloatValue:upper at:PARM_MAX];
[parmForm setFloatValue:value at:PARM_VALUE];
[parmForm setFloatValue:lower at:PARM_MIN];
[parmForm setFloatValue:step at:PARM_STEP];
[[parmForm window] disableDisplay];
[parmSlider setMaxValue:upper];
[parmSlider setMinValue:lower];
[parmSlider setFloatValue:value];
[[parmForm window] reenableDisplay];
[[parmForm window] displayIfNeeded];
return self;
}
- parmFormChanged:sender
{
Storage *parmList;
fitParm *parm;
parm_t type;
type = [parmRadio selectedRow];
parmList = [plotFunction parmListOfType:type];
parm = [parmList elementAt:pfIndex];
if ( !parm ) return self;
parm->upper_limit = [parmForm floatValueAt:PARM_MAX];
parm->value = [parmForm floatValueAt:PARM_VALUE];
parm->lower_limit = [parmForm floatValueAt:PARM_MIN];
parm->step_size = [parmForm floatValueAt:PARM_STEP];
[plotFunction updateFromType:&type];
[self updatePlot];
[self updateChiSq];
return self;
return self;
}
- changeFix:sender
{
Storage *parmList;
fitParm *parm;
parm_t type;
int row;
BOOL yesno;
type = [parmRadio selectedRow];
parmList = [plotFunction parmListOfType:type];
parm = [parmList elementAt:pfIndex];
row = [sender selectedRow];
switch (row) {
case VALUE_TAG:
yesno = [[fixMatrix cellAt:VALUE_TAG :0] state];
parm->fixedValue = yesno;
if (yesno) {
parm->step_size = 0.0;
} else {
parm->step_size = 0.01*(parm->upper_limit - parm->lower_limit);
if (parm->step_size == 0.0) parm->step_size = 0.1;
}
[[parmForm cellAt:PARM_VALUE :0] setEnabled:!(yesno)];
[[parmForm cellAt:PARM_STEP :0] setEnabled:!(yesno)];
[self updateDoF];
break;
case LIMITS_TAG:
yesno = [[fixMatrix cellAt:LIMITS_TAG :0] state];
parm->fixedLimits = yesno;
[[parmForm cellAt:PARM_MAX :0] setEnabled:!(yesno)];
[[parmForm cellAt:PARM_MIN :0] setEnabled:!(yesno)];
break;
}
return self;
}
- funcRadioChanged:sender
{
unsigned int functype;
functype = [funcRadio selectedCol];
switch (functype) {
case FUNC_ALL:
[addButton setTitle:"Add"];
[addButton setAction:@selector(add:)];
break;
case FUNC_PLOT:
[addButton setTitle:"Remove"];
[addButton setAction:@selector(remove:)];
break;
default:
;
}
[self updateBrowser];
[self browserClicked:self];
return self;
}
- parmRadioChanged:sender
{
List *funcList; /* current function List */
int type;
if ( !selectedPlot ) return self;
type = [parmRadio selectedRow];
funcList = [selectedPlot functionList];
[funcList makeObjectsPerform:@selector(updateFromType:)with:(id)&type];
[self updatePlot]; /* updating the plot will force the rest */
[self updateChiSq];
[self updateParmForm];
return self;
}
- fitTypeChanged:sender
{
List *funcList;
enum FCNType_t type;
if (!selectedPlot ) return self;
funcList = [selectedPlot functionList];
if (!funcList ) return self;
type = (enum FCNType_t) [[fitTypeMatrix selectedCell] tag];
[funcList makeObjectsPerform:@selector(setFitType:) with:(id)&type];
if ( type == FIT_MAXLIKE )
[emptyBinButton setEnabled:NO];
else if (h_getDispType( [selectedPlot histDisplay] ) == HISTOGRAM)
[emptyBinButton setEnabled:YES];
[self updateChiSq];
return self;
}
- ebTypeChanged:sender
{
List *funcList;
int type;
if (!selectedPlot ) return self;
funcList = [selectedPlot functionList];
if (!funcList ) return self;
type = [emptyBinMatrix selectedRow];
[funcList makeObjectsPerform:@selector(setEbType:) with:(id)&type];
[self updateChiSq];
return self;
}
- plotFuncLineStyleChanged:sender
{
List *funcList;
int type;
id matrix;
int i;
PFunction *func;
if (!selectedPlot ) return self;
funcList = [selectedPlot functionList];
if (!funcList ) return self;
type = [[plotFuncMatrix selectedCell] tag];
matrix = [funcBrowser matrixInColumn:0];
i = [matrix selectedRow];
if (i < 0) return self;
func = [funcList objectAt:i];
[func setFuncLineStyle:&type];
[self updatePlotMatrix];
[self updatePlot];
[[[graphicView window] delegate] dirty:self];
return self;
}
- plotFSumLineStyleChanged:sender
{
display disp;
int type;
if (!selectedPlot ) return self;
disp = [selectedPlot histDisplay];
type = [[plotFSumMatrix selectedCell] tag];
h_setFSumLineStyle(disp, (linestyle_t)type);
[self updatePlotMatrix];
[self updatePlot];
[[[graphicView window] delegate] dirty:self];
return self;
}
- doFit:sender
{
List *myslist;
int scount;
Graphic *g;
id s1, s2;
NXRect bbox;
myslist = [graphicView selectedGraphics];
scount = [myslist count];
if (scount == 1)
[self doSingleFit:sender];
else {
if (NXRunAlertPanel("Multi-plot Fit",
"Do you really want to fit all plots in the selection?",
"Yes", "NO", NULL)
!= NX_ALERTDEFAULT) {
return self;
}
s1 = selectedPlot; /* save values and restore after */
s2 = firstPlot;
while (scount--) {
g = [myslist objectAt:scount];
if ([g isKindOf:[Plot class]]) {
selectedPlot = (Plot *)g;
firstPlot = (Plot *)g;
[g getBounds:&bbox];
[graphicView scrollRectToVisible:&bbox];
[self doSingleFit:sender];
}
}
selectedPlot = s1;
firstPlot = s2;
}
return self;
}
- addToAvailFuncs:sender
{
OpenPanel *openpanel = [[OpenPanel new] allowMultipleFiles:YES];
Matrix *matrix;
const char *const fileTypes[2] = { "m", NULL };
const char *directory;
const char *const *files;
int row;
if ([openpanel runModalForTypes:fileTypes]) {
directory = [openpanel directory];
files = [openpanel filenames];
while (*files) {
[self loadFunction:directory :*files];
files++;
}
}
[theInspector showPanelWithName:PANEL_NAME];
[theInspector orderFrontPanel:self];
[[funcRadio selectCellAt:0 :FUNC_ALL] sendAction];
matrix = [funcBrowser matrixInColumn:0];
row = [matrix cellCount] -1;
[matrix selectCellAt:row :0];
[matrix scrollCellToVisible:row :0];
return self;
}
- startArchivingTo:(const char *)directory
{
List *plotList;
List *funcList;
List *saveFuncList;
Plot *plot;
PFunction *func, *dynfunc;
const char *filename;
unsigned int i, j, k;
if ( !(k = [dynamFuncList count]) ) return self;
saveFuncList = [[List allocFromZone:[self zone]] initCount:0];
plotList = [graphicView plotList];
if ( !plotList ) return self;
if ( !(i = [plotList count]) ) return self;
while ( i-- ) {
plot = [plotList objectAt:i];
funcList = [plot functionList];
if ( funcList && (j = [funcList count]) ) {
while ( j-- ) {
func = [funcList objectAt:j];
k = [dynamFuncList count];
while ( k-- ) {
dynfunc = [dynamFuncList objectAt:k];
if ( [dynfunc isKindOf:[func class]] ) {
[saveFuncList addObjectIfAbsent:dynfunc];
}
}
}
}
}
if ( !(i = [saveFuncList count]) ) return self;
while ( i-- ) {
dynfunc = [saveFuncList objectAt:i];
filename = [dynfunc filename];
[dynfunc writeFile:directory :filename];
}
return self;
}
- startUnarchivingFrom:(const char *)directory
{
DIR *dirp;
struct direct *dp;
char *ptr;
dirp = opendir( directory );
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
ptr = strrchr( dp->d_name, '.' );
if ( ptr && !strcmp(ptr, ".m") ) {
[self loadFunction:directory :dp->d_name];
}
}
closedir(dirp);
return self;
}
/* Internal methods */
- fetchMinuit
{
List *funcList;
Storage *parmList;
PFunction *func;
fitParm *parm;
parm_t type;
unsigned int i, j, k, num_funcs, num_parms;
type = [parmRadio selectedRow];
if ( type != FIT_VALUES ) return self;
funcList = [selectedPlot functionList];
num_funcs = [funcList count];
k = 0;
for ( i = 0; i < num_funcs; i++ ) {
func = [funcList objectAt:i];
parmList = [func parmListOfType:type];
num_parms = [parmList count];
for ( j = 0; j < num_parms; j++ ) {
parm = [parmList elementAt:j];
k++;
[fitter getParms:parm at:k];
}
[func updateFromType:&type];
}
return self;
}
- loadParams:(double *)param
{
List *funcList;
Storage *parmList;
PFunction *func;
fitParm *parm;
parm_t type;
int i, j, k, num_funcs, num_parms;
type = [parmRadio selectedRow];
funcList = [selectedPlot functionList];
num_funcs = [funcList count];
k = 0;
for ( i = 0; i < num_funcs; i++ )
{
func = [funcList objectAt:i];
parmList = [func parmListOfType:type];
num_parms = [parmList count];
for ( j = 0; j < num_parms; j++ )
{
parm = [parmList elementAt:j];
parm->value = param[k++];
}
[func updateFromType:&type];
}
return self;
}
- setParmType:(int)type
{
[[parmRadio cellAt:type :0] setEnabled:YES ];
[parmRadio selectCellAt:type :0 ];
return self;
}
- updatePlot
{
[graphicView graphicsPerformNOP:selectedPlot];
[[[graphicView window] flushWindow] makeKeyWindow];
return self;
}
- (List *) functionList
{
List *funcList;
unsigned int functype;
functype = [funcRadio selectedCol];
switch (functype) {
case FUNC_ALL:
funcList = availFuncList;
break;
case FUNC_PLOT:
funcList = [selectedPlot functionList];
break;
default:
funcList = nil;
}
return funcList;
}
/* Methods for updating Inspector */
- fullUpdate
{
graphtype_t type;
if ( [selectedPlot isKindOf:[Plot class]] ) {
[addButton setEnabled:YES];
} else {
[addButton setEnabled:NO];
}
type = h_getDispType( [selectedPlot histDisplay] );
if ( !( type == XYPLOT || type == STRIPCHART || type == HISTOGRAM ) ) {
[addButton setEnabled:NO];
}
[self updateDispType:selectedPlot];
if ( [selectedPlot hasFunction] ) {
[funcRadio selectCellAt:0 :FUNC_PLOT];
} else {
[funcRadio selectCellAt:0 :FUNC_ALL];
}
[self updateParmType];
[self funcRadioChanged:self];
[self browserClicked:self];
[self updatePlotMatrix];
// update chi^2 AFTER updating parmType
if ( [selectedPlot hasFunction] ) {
[self updateChiSq];
}
[self checkFitValid];
lastPlot = selectedPlot;
[self updateBrowser];
return self;
}
- updateView
{
if ( selectedPlot != lastPlot ) [self fullUpdate];
return self;
}
- updateEmptySelection
{
if ( lastPlot == nil ) return self;
[addButton setEnabled:NO];
[self enableButtons:NO];
[self updateBrowser];
[self updatePlotMatrix];
[self updateChiSq];
lastPlot = nil;
return self;
}
- updateMultiSelection
{
if ( lastPlot == nil ) return self;
[addButton setEnabled:NO];
[self updateBrowser];
lastPlot = nil;
return self;
}
- updateBrowser
{
[funcBrowser loadColumnZero];
[funcBrowser addColumn];
[funcBrowser displayAllColumns];
return self;
}
- updateChiSq
{
List *funcList;
Storage *parmList;
PFunction *func;
fitParm *parm;
double *values;
double fcnValue = 0.0;
unsigned int i, j, k, num_funcs, num_parms, tot_parms;
parm_t type;
funcList = [selectedPlot functionList];
if (!funcList)
{
[chiSqForm setEnabled:NO];
return self;
} else {
[chiSqForm setEnabled:YES];
}
[self getFitData];
type = [parmRadio selectedRow];
funcList = [selectedPlot functionList];
num_funcs = [funcList count];
tot_parms = 0;
for ( i = 0; i < num_funcs; i++ ) {
func = [funcList objectAt:i];
tot_parms += [func numberArgs];
}
NX_MALLOC( values, double, tot_parms );
k = 0;
for ( i = 0; i < num_funcs; i++ ) {
func = [funcList objectAt:i];
parmList = [func parmListOfType:type];
num_parms = [parmList count];
for ( j = 0; j < num_parms; j++ ) {
parm = [parmList elementAt:j];
values[k++] = parm->value;
}
}
minuitfcn_(&fcnValue, values, &tot_parms, &fcnParms);
[self updateChiSq:fcnValue];
[self updateDoF];
NX_FREE(values);
return self;
}
- updateParmForm
{
Storage *parmList;
fitParm *parm;
parm_t type;
type = [parmRadio selectedRow];
parmList = [plotFunction parmListOfType:type];
parm = [parmList elementAt:pfIndex];
if ( !parm ) return self;
[parmForm setFloatValue:parm->upper_limit at:PARM_MAX];
[parmForm setFloatValue:parm->value at:PARM_VALUE];
[parmForm setFloatValue:parm->lower_limit at:PARM_MIN];
if ( type == FIT_VALUES ) {
[parmForm setTitle:"Error" at:PARM_STEP];
[parmForm setFloatValue:parm->error at:PARM_STEP];
} else {
[parmForm setTitle:"Step" at:PARM_STEP];
[parmForm setFloatValue:parm->step_size at:PARM_STEP];
}
[[parmForm cellAt:PARM_VALUE :0] setEnabled:!(parm->fixedValue)];
[[parmForm cellAt:PARM_STEP :0] setEnabled:!(parm->fixedValue)];
[[parmForm cellAt:PARM_MAX :0] setEnabled:!(parm->fixedLimits)];
[[parmForm cellAt:PARM_MIN :0] setEnabled:!(parm->fixedLimits)];
[[fixMatrix cellAt:VALUE_TAG :0] setState:parm->fixedValue ];
[[fixMatrix cellAt:LIMITS_TAG :0] setState:parm->fixedLimits];
[fixMatrix display];
[[parmSlider window] disableDisplay];
[parmSlider setMaxValue: [parmForm floatValueAt:PARM_MAX]];
[parmSlider setMinValue: [parmForm floatValueAt:PARM_MIN]];
[parmSlider setFloatValue:[parmForm floatValueAt:PARM_VALUE]];
[[parmSlider window] reenableDisplay];
[parmSlider display];
return self;
}
- updateParmType
{
List *funcList;
PFunction *func;
parm_t type;
if ( !selectedPlot ) return self;
funcList = [selectedPlot functionList];
if ( funcList && [funcList count] ) {
func = [funcList lastObject];
type = [func currentType];
[self setParmType:type];
}
return self;
}
- updatePlotMatrix
{
display disp;
List *funcList;
PFunction *func;
int itype;
id matrix;
int i;
if (!selectedPlot || [funcRadio selectedCol] == FUNC_ALL) {
[plotFuncButton setEnabled:NO];
[plotFSumButton setEnabled:NO];
return self;
}
disp = [selectedPlot histDisplay];
h_setPlotFuncs(disp, 1); // just to be sure
h_setPlotFuncSum(disp, 1);
funcList = [selectedPlot functionList];
matrix = [funcBrowser matrixInColumn:0];
i = [matrix selectedRow];
if (i < 0) {
[plotFuncButton setEnabled:NO];
} else {
[plotFuncButton setEnabled:YES];
func = [funcList objectAt:i];
itype = [func funcLineStyle];
[plotFuncMatrix selectCellWithTag:itype];
[plotFuncButton setTitle:lineStyleTitle[itype]];
}
itype = h_getFSumLineStyle(disp);
[plotFSumMatrix selectCellWithTag:itype];
[plotFSumButton setTitle:lineStyleTitle[itype]];
[plotFSumButton setEnabled:YES];
return self;
}
- updateDispType:(Plot *)plot
{
List *funcList;
PFunction *func;
graphtype_t dtype;
int itype;
dtype = h_getDispType( [plot histDisplay] );
[ [fitTypeMatrix findCellWithTag:FIT_ONESAMPLE] setEnabled:YES ];
if ( dtype == XYPLOT || dtype == STRIPCHART ) {
[ [fitTypeMatrix findCellWithTag:FIT_INTEGRAL] setEnabled:NO ];
// [ [fitTypeMatrix findCellWithTag:FIT_INTFIXEDNORM] setEnabled:NO ];
[ [fitTypeMatrix findCellWithTag:FIT_AVERAGE] setEnabled:YES ];
// [ [fitTypeMatrix findCellWithTag:FIT_MAXLIKE] setEnabled:NO ];
[emptyBinButton setEnabled:NO];
} else {
[ [fitTypeMatrix findCellWithTag:FIT_INTEGRAL] setEnabled:YES ];
// [ [fitTypeMatrix findCellWithTag:FIT_INTFIXEDNORM] setEnabled:YES ];
[ [fitTypeMatrix findCellWithTag:FIT_AVERAGE] setEnabled:NO ];
// [ [fitTypeMatrix findCellWithTag:FIT_MAXLIKE] setEnabled:YES ];
[emptyBinButton setEnabled:YES];
}
funcList = [plot functionList];
itype = [fitTypeMatrix selectedRow];
if ( funcList && [funcList count] ) {
func = [funcList objectAt:0];
itype = [func ebType];
[emptyBinMatrix selectCellAt:itype :0];
[emptyBinButton setTitle:emptyBinTitle[itype]];
itype = [func fitType];
[fitTypeMatrix selectCellWithTag:itype];
[fitTypeButton setTitle:doFitTitle[itype]];
}
if (! [[fitTypeMatrix selectedCell] isEnabled]) {
itype = FIT_ONESAMPLE;
[fitTypeMatrix selectCellWithTag:itype];
[fitTypeButton setTitle:doFitTitle[itype]];
[self fitTypeChanged:self];
}
if ( itype == FIT_MAXLIKE ) [emptyBinButton setEnabled:NO];
return self;
}
/* Delegate Methods for NXBrowsers */
- (int) browser: sender fillMatrix: matrix inColumn: (int) column
{
unsigned int count;
switch (column) {
case 0:
count = [self fillColumnZeroMatrix:matrix];
break;
case 1:
count = [self fillColumnOneMatrix:matrix];
break;
default:
count = 0;
}
return count;
}
- (int) fillColumnZeroMatrix:matrix
{
List *funcList;
NXBrowserCell *aCell;
unsigned int i, count;
funcList = [self functionList];
count = [funcList count];
for ( i = 0; i < count; i++ ) {
[matrix insertRowAt:i];
aCell = [matrix cellAt:i :0];
[aCell setStringValue:[[funcList objectAt:i] title]];
[aCell setLeaf:NO];
[aCell setLoaded:YES];
}
return count;
}
- (int) fillColumnOneMatrix:matrix
{
List *funcList;
Matrix *funcMatrix;
NXBrowserCell *aCell;
PFunction *func;
unsigned int count;
int i;
count = 0;
funcMatrix = [funcBrowser matrixInColumn:0];
i = [funcMatrix selectedRow];
if ( i < 0 ) {
i = 0;
}
funcList = [self functionList];
func = [funcList objectAt:i];
if ( !func ) return count;
count = [func numberArgs];
for ( i = 0; i < count; i++ ) {
[matrix insertRowAt:i];
aCell = [matrix cellAt:i :0];
[aCell setStringValue:[func argNameAt:i]];
[[aCell setLeaf:YES] setLoaded:YES];
}
return count;
}
@end
/* Private Methods */
@implementation InspectPFunc(Private)
- (NXBundle *) compile:(const char *)directory :(const char *)file
{
NXBundle *bundle;
struct stat statbuf;
char basename[MAXNAMLEN+1];
char ofile[MAXPATHLEN+1];
char sys_cmd[MAXPATHLEN+1];
char inclpath[MAXPATHLEN+1];
char *ptr;
BOOL ok;
strcpy( basename, file );
ptr = strrchr( basename, '.' );
*ptr = '\0';
sprintf( ofile, "/tmp/%s", basename );
strcat( ofile, ".bundle");
if ( stat(ofile, &statbuf) ) {
sprintf( sys_cmd, "mkdir %s", ofile );
if ( system( sys_cmd ) ) {
return nil;
}
}
strcat( ofile, "/");
strcat( ofile, basename );
chdir(directory);
ok = [[NXBundle mainBundle] getPath:inclpath
forResource:"PFunction" ofType:"h"];
if ( !ok ) {
NXRunAlertPanel( "Error",
"Can not find needed include files in HippoDraw.app",
"OK", NULL, NULL );
return nil;
}
ptr = strrchr(inclpath, '/');
*ptr = '\0';
#ifdef GPPCOMPILE
sprintf(sys_cmd,
"cc -ObjC++ -g -O -Wall -I%s -I%s -c %s -o %s.o",
inclpath, GPPINCLUDEDIR, file, ofile );
#else
sprintf(sys_cmd,
"cc -g -O -Wall -I%s -c %s -o %s.o", inclpath, file, ofile );
#endif
if ( system( sys_cmd ) ) {
return nil;
}
#ifdef GPPCOMPILE
sprintf( sys_cmd, "ld -r -o %s %s.o %s", ofile, ofile, GPPLIBRARY );
#else
sprintf( sys_cmd, "ld -r -o %s %s.o", ofile, ofile);
#endif
if ( system( sys_cmd ) ) {
return nil;
}
sprintf( sys_cmd, "rm -f %s.o", ofile);
system( sys_cmd );
ptr = strrchr( ofile, '/' );
*ptr = '\0';
bundle = [[NXBundle alloc] initForDirectory:ofile];
return bundle;
}
- enableButtons:(BOOL) flag
{
[parmForm setEnabled:flag];
[parmSlider setEnabled:flag];
[parmSliderU setEnabled:flag];
[parmSliderD setEnabled:flag];
[fixMatrix setEnabled:flag];
[doFitButton setEnabled:flag];
return self;
}
- getFitData
{
id funcList = [selectedPlot functionList];
if (!funcList) return self;
if (fitNtuple == NULL) fitNtuple = h_new( 4 );
fcnParms.disp = [selectedPlot histDisplay];
fcnParms.fitType = (FCNType) [[funcList objectAt:0] fitType];
h_fillWithFitData(fcnParms.disp, fitNtuple,
(fcnParms.fitType == FIT_MAXLIKE) );
fcnParms.x = h_getNtColumn( fitNtuple, 0 );
fcnParms.xerr = h_getNtColumn( fitNtuple, 1 );
fcnParms.y = h_getNtColumn( fitNtuple, 2 );
fcnParms.yerr = h_getNtColumn( fitNtuple, 3 );
fcnParms.npts = h_getNtNdata( fitNtuple );
fcnParms.ebType = [[funcList objectAt:0] ebType];
return self;
}
- loadFunction:(const char *)directory :(const char *)file
{
NXBundle *bundle;
PFunction *func;
bundle = [self compile:directory :file];
if ( bundle ) {
func = [[[bundle principalClass] allocFromZone:[self zone]] init];
[availFuncList addObject:func];
[dynamFuncList addObject:func];
[func readFile:directory :file];
}
return self;
}
- loadMinuit
{
List *funcList; /* current function List */
int nFunc;
Storage *parmList;
int nParms;
int type;
int i, j;
int iparm = 1; /* the Minuit index of a parameter */
fitParm *parm;
if ( ![selectedPlot hasFunction] ) return self;
[fitter clearParms];
funcList = [selectedPlot functionList];
nFunc = [funcList count];
type = [parmRadio selectedRow];
for ( i = 0; i < nFunc; i++) {
parmList = [[funcList objectAt:i] parmListOfType:type];
nParms = [parmList count];
for ( j = 0; j < nParms; j++ ) {
parm = (fitParm *)[parmList elementAt:j];
[fitter setParms:parm at:iparm];
iparm++;
}
}
/*
* For fixed normalization fits, add Lagrange multiplier.
*/
if ([[funcList objectAt:0] fitType] == FIT_INTFIXEDNORM) {
[fitter setLagrangeAt:iparm];
}
return self;
}
- updateChiSq: (float) chi2
{
[chiSqForm setDoubleValue:chi2 at:CHISQ_POS];
// don't update DoF form. it won't have changed
return self;
}
- updateDoF
{
List *funcList = [selectedPlot functionList];
Storage *parmList;
PFunction *func;
fitParm *parm;
parm_t type = [parmRadio selectedRow];
int i, j, num_parms;
int dof;
if (fitNtuple == NULL) [self getFitData];
dof = h_getNtNdata( fitNtuple );
if (!funcList)
{
[chiSqForm setEnabled:NO];
return self;
} else {
[chiSqForm setEnabled:YES];
}
for ( i = [funcList count]-1; i>=0 ; i-- ) {
func = [funcList objectAt:i];
dof -= [func numberArgs];
parmList = [func parmListOfType:type];
num_parms = [parmList count];
for ( j = 0; j < num_parms; j++ ) {
parm = [parmList elementAt:j];
if (parm->fixedValue) dof++;
}
}
[chiSqForm setIntValue:dof at:DOF_POS];
return self;
}
- doSingleFit:sender
{
List *funcList; /* current function List */
parm_t type;
int i;
if ( !selectedPlot ) return self;
funcList = [selectedPlot functionList];
if ( !funcList ) return self;
if ( !fitter ) {
[hDraw loadMinuit:self];
}
[sender getFrame:&stopButtonFrame];
old_fcn_value = FLT_MAX;
type = [parmRadio selectedRow];
[funcList makeObjectsPerform:@selector(copyToInitial:) with:(id)&type];
[self getFitData];
/*
* check if there are bins with error = 0 and flag if desired.
*/
if ( (fcnParms.ebType == EB_NOFIT) &&
(fcnParms.fitType == FIT_ONESAMPLE
|| fcnParms.fitType == FIT_INTEGRAL
|| fcnParms.fitType == FIT_INTFIXEDNORM) ) {
for ( i = 0; i < fcnParms.npts; i++ ) {
if ( fcnParms.y[i] == 0.0 ) {
NXRunAlertPanel( "Alert",
"Histogram has bins that are empty. "
"No fit will been done. "
"Check empty bins options in Fit Options Inspector.",
NULL, NULL, NULL );
return self;
}
}
}
[self loadMinuit];
[funcList makeObjectsPerform:@selector(copyToFitted:) with:(id)&type];
[self setParmType:FIT_VALUES];
[fitter doFitWith:&fcnParms];
// Fit is done. Update the displays
NXBeep();
[self fetchMinuit];
[self updateParmForm];
[self updateChiSq];
[self updatePlot];
[selectedPlot setFitted:YES];
[[[graphicView window] delegate] dirty:self];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.