/* Copyright The Numerical Algorithms Group Ltd., Oxford, UK, 1998 This module accepts some 1D lattices and plots the data on a set of axes. Written by Jeremy Walton (jeremyw@nag.co.uk). 24 September 1998. */ #include #include #include #include #include #include #include #include #include #include #include "LineGraph.uf.h" #ifdef __cplusplus extern "C" { #endif void init(void); void getOptions(int select); void setPlotDataOptions(int line); #ifdef __cplusplus } #endif #define MAXCOL 7 #define RED { 1.0, 0.0, 0.0 } #define GRN { 0.0, 1.0, 0.0 } #define BLU { 0.0, 0.0, 1.0 } #define YEL { 1.0, 1.0, 0.0 } #define MAG { 1.0, 0.0, 1.0 } #define CYA { 0.0, 1.0, 1.0 } #define WHI { 1.0, 1.0, 1.0 } #define BLK { 0.0, 0.0, 0.0 } /* This is the maximum number of curves that we can plot (that */ /* we have parameter ports (Color, Style, Label and Data) for. */ /* This limit can be changed, but the number of parameter ports */ /* must always correspond to it. */ #define MAXLINE 6 typedef struct { int id; int drawingmode; char label[50]; int colorindex; } Plot; static Plot plot[MAXLINE]; static int line; void init() { /* Initialisation routine, called once when the module is */ /* dragged into the map editor. */ /* Initialise the geometry library. */ /* cxGeoInit(); */ cxGeoMSInit(); /* line counts the number of lines plotted. */ line = 0; } void compute ( cxGeometry **geoOut, int logx, int gridx, float Xmin, int CalcXmin, float Xmax, int CalcXmax, unsigned char *xlabel_, float xscale, int logy, int gridy, float Ymin, int CalcYmin, float Ymax, int CalcYmax, unsigned char *ylabel_, float yscale, unsigned char *title_, unsigned char *legend_, int dataselect) { char *xlabel = (char*)xlabel_; char *ylabel = (char*)ylabel_; char *title = (char*)title_; char *legend = (char*)legend_; /* Numerical values for colours - this should probably be in an include. */ /* float red[3] = RED; */ /* float grn[3] = GRN; */ /* float blu[3] = BLU; */ float yel[3] = YEL; /* float mag[3] = MAG; */ /* float cya[3] = CYA; */ float whi[3] = WHI; float blk[3] = BLK; /* Location of the plot. */ float plotBottomLeft[2] = {-0.361, -0.2}; float plotTopRight[2] = {0.808, 0.523}; /* the golden ratio, to make the plot look nice. */ /* Location of the legend. */ float legBottomLeft[2], legTopRight[2]; char message[50]; float titleSize = 0.2; float *coordVal,*X, *Y, aspect, xrange, yrange; void *dataVal; long nDim, *dims, nDataVar, nCoordVar; cxCoordType coordType; cxPrimType primType; int i, nconn, jconn, *links, iplot, ivar, nvar, vDim, negx, negy; cxLattice **latList; float xmin = FLT_MAX; float ymin = FLT_MAX; float xmax = -FLT_MAX; float ymax = -FLT_MAX; /* Read the plotting options from the control panel. */ getOptions(dataselect); /* Create a new geometry buffer and select it. Start a new plot. */ *geoOut = cxGeoNew(); cxGeoBufferSelect(*geoOut); cxGeoRoot(); cxGeoDelete(); cxGeoPlotBegin(); /* For each limit, determine whether we're calculating it from the data. */ /* If so, then disable the input widget. The limit is automatically */ /* calculated in the call to cxGeoPlotDataAdd, below (actually, we */ /* also calculate it from the data in order to adjust the limits on */ /* the limits widgets. Otherwise, enable the widget and set the limit. */ if( CalcXmin ) { cxInWdgtDisable("X Min"); negx = 0; } else { cxInWdgtEnable("X Min"); cxGeoPlotFloatSet(CX_GEO_PLOT_XMIN, Xmin); negx = ( Xmin <= 0.0 ); } if( CalcXmax ) cxInWdgtDisable("X Max"); else { cxInWdgtEnable("X Max"); cxGeoPlotFloatSet(CX_GEO_PLOT_XMAX, Xmax); } if( CalcYmin ) { cxInWdgtDisable("Y Min"); negy = 0; } else { cxInWdgtEnable("Y Min"); cxGeoPlotFloatSet(CX_GEO_PLOT_YMIN, Ymin); negy = ( Ymin <= 0.0 ); } if( CalcYmax ) cxInWdgtDisable("Y Max"); else { cxInWdgtEnable("Y Max"); cxGeoPlotFloatSet(CX_GEO_PLOT_YMAX, Ymax); } /* Initialise counters. */ line = 0; /* Get the number of connections to the lattice port, and get the array */ /* of lattices which are on this port. */ nconn = cxInputConnectsGet(cxInputPortOpen("Input Values")); cxInputDataGetV(cxInputPortOpen("Input Values"), (void ***)&latList); cxInputDataConnIDGetV(cxInputPortOpen("Input Values"), &links); /* Get each of the lattices in turn. */ for( jconn=0; jconn MAXLINE ) { sprintf(message, "Can only plot %d lines", MAXLINE); cxModAlert(message); nvar = MAXLINE - line; } else nvar = nDataVar; /* Allocate space for X, an array containing the coordinates of each */ /* node in the lattice. */ vDim = (int)cxDimsProd(nDim, dims, nCoordVar) / nCoordVar; /* If it's a uniform lattice, calculate the coordinates explicitly. */ /* Otherwise (perimeter or curvilinear) just point X at the coordinates. */ if( coordType == cx_coord_uniform ) { float step; X = (float *)malloc(sizeof(float) * vDim); step = (coordVal[1] - coordVal[0]) / (vDim-1); for( i=0; i xmax) xmax = X[i]; if( Y[i] < ymin) ymin = Y[i]; if( Y[i] > ymax) ymax = Y[i]; } /* Add this data and coordinates to the plot, remember the connection */ /* that it came in on, and set the attributes for this line. */ iplot = cxGeoPlotDataAdd(vDim, X, Y); plot[line].id = links[jconn]; setPlotDataOptions(line); /* Add the label for this data to the legend (whether or not */ /* it's going to be displayed). */ cxGeoPlotDataStrSet(CX_GEO_DATA_LABEL, plot[line].label); } free(Y); if (X != coordVal) free(X); } /* Now use the data limits to set the limits on the widgets */ /* attached to the limit parameters (!) */ xrange = xmax-xmin; yrange = ymax-ymin; cxInWdgtDblMinMaxSet("X Min", xmin-0.1*xrange, xmax+0.1*xrange); cxInWdgtDblMinMaxSet("X Max", xmin-0.1*xrange, xmax+0.1*xrange); cxInWdgtDblMinMaxSet("Y Min", ymin-0.1*yrange, ymax+0.1*yrange); cxInWdgtDblMinMaxSet("Y Max", ymin-0.1*yrange, ymax+0.1*yrange); if( CalcXmin ) cxInWdgtDblSet("X Min", xmin); if( CalcXmax ) cxInWdgtDblSet("X Max", xmax); if( CalcYmin ) cxInWdgtDblSet("Y Min", ymin); if( CalcYmax ) cxInWdgtDblSet("Y Max", ymax); /* Set the attributes for the plot. */ cxGeoPlotStrSet(CX_GEO_PLOT_XAXIS_LABEL, xlabel); cxGeoPlotStrSet(CX_GEO_PLOT_YAXIS_LABEL, ylabel); aspect = (plotTopRight[1] - plotBottomLeft[1])/(plotTopRight[0] - plotBottomLeft[0]); cxGeoPlotFloatSet(CX_GEO_PLOT_XAXIS_SCALE, xscale*aspect); cxGeoPlotFloatSet(CX_GEO_PLOT_YAXIS_SCALE, yscale); cxGeoPlotColorSet(CX_GEO_PLOT_AXIS_COLOR, whi); cxGeoPlotOptionSet(CX_GEO_PLOT_XAXIS_ARROW, TRUE); cxGeoPlotOptionSet(CX_GEO_PLOT_YAXIS_ARROW, TRUE); cxGeoPlotOptionSet(CX_GEO_PLOT_XGRID_VISIBLE, gridx == 0 ? FALSE : TRUE); cxGeoPlotOptionSet(CX_GEO_PLOT_YGRID_VISIBLE, gridy == 0 ? FALSE : TRUE); cxGeoPlotFloatSet(CX_GEO_PLOT_AXIS_LINE_WIDTH, 2.0); cxGeoPlotFloatSet(CX_GEO_PLOT_GRID_LINE_WIDTH, 1.0); cxGeoPlotOptionSet(CX_GEO_PLOT_GRID_PATTERN, 0xf0f0); /* Log axes? Ensure the values are > 0, otherwise switch back to linear axes. */ if( logx && negx ) { cxModAlert("Non-positive X values detected - can't have log axis"); cxGeoPlotOptionSet(CX_GEO_PLOT_XAXIS_TYPE, CX_GEO_PLOT_LINEAR_AXIS); cxInWdgtLongSet("LogX", 0); } else cxGeoPlotOptionSet(CX_GEO_PLOT_XAXIS_TYPE, logx == 0 ? CX_GEO_PLOT_LINEAR_AXIS : CX_GEO_PLOT_LOG_AXIS); if( logy && negy ) { cxModAlert("Non-positive Y values detected - can't have log axis"); cxGeoPlotOptionSet(CX_GEO_PLOT_YAXIS_TYPE, CX_GEO_PLOT_LINEAR_AXIS); cxInWdgtLongSet("LogY", 0); } else cxGeoPlotOptionSet(CX_GEO_PLOT_YAXIS_TYPE, logy == 0 ? CX_GEO_PLOT_LINEAR_AXIS : CX_GEO_PLOT_LOG_AXIS); /* Add the legend, if necessary. */ if( strlen(legend) > 0 ) { legBottomLeft[0] = -0.758-yscale*0.0723; legBottomLeft[1] = 0.089-line*0.0361; legTopRight[0] = -0.47-yscale*0.0723; legTopRight[1] = 0.234+line*0.0361; cxGeoPlotLegendAdd(legBottomLeft, legTopRight, legend); } cxGeoPlotColorSet(CX_GEO_PLOT_LEGEND_BACK_COLOR, blk); cxGeoPlotColorSet(CX_GEO_PLOT_LEGEND_BORDER_COLOR, whi); cxGeoPlotColorSet(CX_GEO_PLOT_LEGEND_TEXT_COLOR, whi); /* Define the position of the plot in 2D space. */ cxGeoPlotDefine(plotBottomLeft, plotTopRight); /* Add the title to the plot, at the top, suitably centered (whether there's */ /* a legend or not). */ cxGeoPlotTextDefine(title, NULL, titleSize, CX_GEO_PLOT_TEXT_CENTER); /* Use enumerated type to keep compiler happy - JPW */ cxGeoColorAdd(1, yel, CX_GEO_PER_DEFAULT); if( strlen(legend) > 0 ) cxGeoTranslate(legBottomLeft[0] + 0.5*(plotTopRight[0] - legBottomLeft[0]), plotTopRight[1]+titleSize*0.9, 0.0); else cxGeoTranslate(plotBottomLeft[0] + 0.5*(plotTopRight[0] - plotBottomLeft[0]), plotTopRight[1]+titleSize*0.9, 0.0); /* Close the buffer and output the new geometry. */ cxGeoBufferClose(*geoOut); } void getOptions(int select) { int i; char portName[20], label[20]; /* Read the values in from the parameter to give the display properties */ /* of each curve. */ for( i=0; i 0 ) cxInWdgtChoiceLabelSet("Select Data", i, plot[i].label); } /* If we've selected a different data set, then show the widgets for */ /* that data set. It's easiest to first hide them all, then show the */ /* selected one. */ if( cxInputDataChanged(cxInputPortOpen("Select Data")) ) { for( i=0; i= 5 && plot[line].drawingmode <= 7 ) cxGeoPlotDataColorSet(CX_GEO_DATA_MARKER_COLOR, color[plot[line].colorindex]); if( plot[line].drawingmode >= 8 ) cxGeoPlotDataColorSet(CX_GEO_DATA_FILL_COLOR, color[plot[line].colorindex]); cxGeoPlotDataFloatSet(CX_GEO_DATA_LINE_WIDTH, 2.0); } void disconnected(const char *name, int id) { int i, k, n, index[MAXLINE]; char portName[20], label[20]; /* Disconnect hook function, called when one of the connections is broken. */ /* We don't want to do anything unless it was the lattice input port that */ /* was disconnected. */ if( strcmp(name, "Input Values") ) return; /* We have to modify the plot parameters database, to remove the entry(s) for */ /* the disconnected lattice. Loop over all the entries, searching for those */ /* that correspond to the disconnected lattice. There may be more than one */ /* of these, but they'll always be consecutive. */ k = 0; for( i=0; i