home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-06-07 | 34.7 KB | 1,417 lines |
- // -------------------------------------------------------------------------------------
- // BarChart.m
- // Martin D. Flynn, NeXT Computer, Inc.
- // -------------------------------------------------------------------------------------
-
- #import <appkit/appkit.h>
- #import <libc.h>
- #import <c.h>
- #import <stdlib.h>
- #import <string.h>
- #import <math.h>
- #import <dpsclient/wraps.h>
- #import <streams/streams.h>
- #import <objc/Storage.h>
- #import <objc/List.h>
- #import "BarChartPS.h"
- #import "BarChart.h"
-
- // -------------------------------------------------------------------------------------
- // archive version number
- #define VERSION 2
-
- // -------------------------------------------------------------------------------------
- // data source types
- #define srcTypeSAMPLE -1
- #define srcTypeNONE 0
- #define srcTypeTEXT 1
- #define srcTypeMATRIX 2
- #define srcTypeSTORAGE 3
- #define srcTypeSTREAM 4
-
- // -------------------------------------------------------------------------------------
- #define X origin.x
- #define Y origin.y
- #define W size.width
- #define H size.height
- #define fontWIDTH(L) [currentFont getWidthOf:L]
-
- // -------------------------------------------------------------------------------------
- // x/y plot/scale conversions
- #define xPOINT(P) (cFlags.xLogScale? [self log:(P)] : (P))
- #define yPOINT(P) (cFlags.yLogScale? [self log:(P)] : (P))
- #define xSCALE(P) (dataScale.x * xPOINT(P))
- #define ySCALE(P) (dataScale.y * yPOINT(P))
- #define xPLOT(P) (axisOrigin.x + xSCALE(P))
- #define yPLOT(P) (axisOrigin.y + ySCALE(P))
-
- // -------------------------------------------------------------------------------------
- @implementation BarChart
-
- // -------------------------------------------------------------------------------------
- // set archive version number
- static BOOL _nibMode = NO;
- + initialize
- {
- [self setVersion:VERSION];
- if (!strcmp([NXApp appName], "InterfaceBuilder")) _nibMode = YES;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // return inspector name
- - (const char*)getInspectorClassName { return "BarChartInspector"; }
-
- // -------------------------------------------------------------------------------------
- // color support
-
- /* convert color to gray */
- static float _colorGray(NXColor color)
- {
- float gray;
- NXConvertColorToGray(color, &gray);
- return gray;
- }
-
- /* set color/gray */
- static void _setColor(NXColor color, BOOL isColor)
- {
- if (isColor) NXSetColor(color); else PSsetgray(_colorGray(color));
- }
-
- // -------------------------------------------------------------------------------------
- // new
- - initFrame:(const NXRect*)r
- {
-
- /* instantiate view */
- self = [super initFrame:r];
- isFirstResponder = NO;
-
- /* init font */
- currentFont = [Font newFont:"Helvetica" size:10.0 matrix:NX_IDENTITYMATRIX];
-
- /* initialize flags */
- memset(&cFlags, 0, sizeof(cFlags));
- cFlags.xShowLabels = YES;
- cFlags.xLogScale = NO;
- cFlags.xGridDraw = NO;
- cFlags.yShowLabels = YES;
- cFlags.yLogScale = NO;
- cFlags.yGridDraw = YES;
- cFlags.drawInColor = NO;
-
- /* initialize colors */
- backgroundColor = NXConvertGrayToColor(NX_LTGRAY);
- axisColor = NXConvertGrayToColor(NX_BLACK);
- yGridColor = NXConvertGrayToColor(NX_WHITE);
- xGridColor = NXConvertGrayToColor(NX_WHITE);
-
- /* default chart (for IB use) */
- dftChartType = chartBAR;
- dftChartColor = NXConvertGrayToColor(NX_DKGRAY);
-
- /* init outlets */
- delegate = self;
- xLabelMatrix = (id)nil;
- yLabelMatrix = (id)nil;
- actionMatrix = (id)nil;
-
- /* init defaut chart */
- dataList = (id)nil;
- dataRange.X = 0.0;
- dataRange.Y = 0.0;
- dataRange.W = 10.0;
- dataRange.H = 5.0;
- tickMark.X = 0.0;
- tickMark.Y = 0.0;
- tickMark.W = 2.0;
- tickMark.H = 1.0;
-
- /* flag for rescaling */
- reScale = YES;
-
- return self;
- }
-
- /* free */
- - free
- {
- if (dataList) [dataList free];
- return [super free];
- }
-
- // -------------------------------------------------------------------------------------
- // access to internal charting data structure list
- // -------------------------------------------------------------------------------------
-
- /* return charting data list */
- - (Storage*)chartList
- {
- return dataList;
- }
-
- /* return number of chart types in list */
- - (int)chartCount
- {
- return [dataList count];
- }
-
- /* return pointer to specific chart item */
- - (chartData_s*)chartDataAt:(int)index
- {
- return (chartData_s*)[dataList elementAt:index];
- }
-
- // -------------------------------------------------------------------------------------
- // log conversion method
- // -------------------------------------------------------------------------------------
-
- /* return log converted data point */
- - (float)log:(float)num
- {
- return num? (float)log10(fabs((double)num)) : 0.0;
- }
-
- /* return antiLog converted data point */
- - (float)exp:(float)num
- {
- return (float)pow((double)10.0, (double)num);
- }
-
- // -------------------------------------------------------------------------------------
- // chart data point conversion
- // -------------------------------------------------------------------------------------
-
- /* convert data point to chart */
- - convertDataPointToChart:(NXPoint*)point
- {
- point->x = xPLOT(point->x);
- point->y = xPLOT(point->y);
- return self;
- }
-
- /* convert chart point to data */
- - convertChartPointToData:(NXPoint*)point
- {
- NXPoint p;
- p.x = (point->x - axisOrigin.x) / dataScale.x;
- p.y = (point->y - axisOrigin.y) / dataScale.y;
- point->x = cFlags.xLogScale? [self exp:p.x] : p.x;
- point->y = cFlags.yLogScale? [self exp:p.y] : p.y;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // set plot options
- // -------------------------------------------------------------------------------------
-
- /* set delegate */
- - setDelegate:anObject
- {
- delegate = anObject;
- return self;
- }
-
- /* return current delegate */
- - delegate
- {
- return delegate;
- }
-
- /* set font (make a new unflipped font copy) */
- // This is done this way on purpose
- - setFont:fontId
- {
- currentFont = [Font newFont:[fontId name] size:[fontId pointSize] matrix:NX_IDENTITYMATRIX];
- reScale = YES;
- return self;
- }
-
- /* return current font (NX_FLIPPEDMATRIX) */
- // This is done this way on purpose (IB can't handle it properly any other way)
- - font
- {
- return [Font newFont:[currentFont name] size:[currentFont pointSize]];
- }
-
- /* return isColor status */
- - (BOOL)drawInColor
- {
- return cFlags.drawInColor;
- }
-
- /* set background transparent */
- - setBackgroundTransparent:(BOOL)flag
- {
- cFlags.transparent = flag;
- return self;
- }
-
- /* return background transparent state */
- - (BOOL)backgroundTransparent
- {
- return cFlags.transparent;
- }
-
- /* set background gray */
- - setBackgroundGray:(float)gray
- {
- backgroundColor = NXConvertGrayToColor(gray);
- cFlags.drawInColor = NO;
- return self;
- }
-
- /* return background gray */
- - (float)backgroundGray
- {
- return _colorGray(backgroundColor);
- }
-
- /* set background color */
- - setBackgroundColor:(NXColor)color
- {
- backgroundColor = color;
- cFlags.drawInColor = YES;
- return self;
- }
-
- /* return background color */
- - (NXColor)backgroundColor
- {
- return backgroundColor;
- }
-
- /* set default chart gray */
- - setDftChartGray:(float)gray
- {
- dftChartColor = NXConvertGrayToColor(gray);
- cFlags.drawInColor = NO;
- return self;
- }
-
- /* return default chart gray */
- - (float)dftChartGray
- {
- return _colorGray(dftChartColor);
- }
-
- /* set default chart gray */
- - setDftChartColor:(NXColor)color
- {
- dftChartColor = color;
- cFlags.drawInColor = YES;
- return self;
- }
-
- /* return default chart color */
- - (NXColor)dftChartColor
- {
- return dftChartColor;
- }
-
- /* set axis gray */
- - setAxisGray:(float)gray
- {
- axisColor = NXConvertGrayToColor(gray);
- cFlags.drawInColor = NO;
- return self;
- }
-
- /* return axis gray */
- - (float)axisGray
- {
- return _colorGray(axisColor);
- }
-
- /* set axis color */
- - setAxisColor:(NXColor)color
- {
- axisColor = color;
- cFlags.drawInColor = YES;
- return self;
- }
-
- /* return axis color */
- - (NXColor)axisColor
- {
- return axisColor;
- }
-
- /* set to draw x grid */
- - setXGridDraw:(BOOL)flag
- {
- cFlags.xGridDraw = flag;
- return self;
- }
-
- /* return x grid drawing status */
- - (BOOL)xGridDraw
- {
- return cFlags.xGridDraw;
- }
-
- /* set x grid gray */
- - setXGridGray:(float)gray
- {
- xGridColor = NXConvertGrayToColor(gray);
- cFlags.drawInColor = NO;
- return self;
- }
-
- /* return x grid gray */
- - (float)xGridGray
- {
- return _colorGray(xGridColor);
- }
-
- /* set x grid color */
- - setXGridColor:(NXColor)color
- {
- xGridColor = color;
- cFlags.drawInColor = YES;
- return self;
- }
-
- /* return x grid color */
- - (NXColor)xGridColor
- {
- return xGridColor;
- }
-
- /* set to draw y grid */
- - setYGridDraw:(BOOL)flag
- {
- cFlags.yGridDraw = flag;
- return self;
- }
-
- /* return y grid drawing status */
- - (BOOL)yGridDraw
- {
- return cFlags.yGridDraw;
- }
-
- /* set y grid gray */
- - setYGridGray:(float)gray
- {
- yGridColor = NXConvertGrayToColor(gray);
- cFlags.drawInColor = NO;
- return self;
- }
-
- /* return y grid gray */
- - (float)yGridGray
- {
- return _colorGray(yGridColor);
- }
-
- /* set y grid color */
- - setYGridColor:(NXColor)color
- {
- yGridColor = color;
- cFlags.drawInColor = YES;
- return self;
- }
-
- /* return y grid color */
- - (NXColor)yGridColor
- {
- return yGridColor;
- }
-
- /* set default chart type */
- - setDftChartType:(int)chartType
- {
- dftChartType = chartType;
- return self;
- }
-
- /* return default chart type */
- - (int)dftChartType
- {
- return dftChartType;
- }
-
- /* data range */
- - setDataRange:(NXRect*)range
- {
- dataRange = *range;
- reScale = YES;
- return self;
- }
-
- /* return data range */
- - (NXRect*)dataRange
- {
- return &dataRange;
- }
-
- /* tick marks (origin currently not used) */
- - setTickMark:(NXRect*)tick
- {
- tickMark = *tick;
- tickMark.X = tickMark.Y = 0.0;
- reScale = YES;
- return self;
- }
-
- /* return tick mark settings */
- - (NXRect*)tickMark
- {
- return &tickMark;
- }
-
- /* set x log scaling */
- - setXLogScaling:(BOOL)flag
- {
- cFlags.xLogScale = flag;
- reScale = YES;
- return self;
- }
-
- /* return x log scaling */
- - (BOOL)xLogScaling
- {
- return cFlags.xLogScale;
- }
-
- /* set y log scaling */
- - setYLogScaling:(BOOL)flag
- {
- cFlags.yLogScale = flag;
- reScale = YES;
- return self;
- }
-
- /* return y log scaling */
- - (BOOL)yLogScaling
- {
- return cFlags.yLogScale;
- }
-
- /* set x show labels */
- - setXShowLabels:(BOOL)flag
- {
- cFlags.xShowLabels = flag;
- reScale = YES;
- return self;
- }
-
- /* return x show labels */
- - (BOOL)xShowLabels
- {
- return cFlags.xShowLabels;
- }
-
- /* set y show labels */
- - setYShowLabels:(BOOL)flag
- {
- cFlags.yShowLabels = flag;
- reScale = YES;
- return self;
- }
-
- /* return y show labels */
- - (BOOL)yShowLabels
- {
- return cFlags.yShowLabels;
- }
-
- /* rotate x axis labels */
- - setXLabelRotate:(BOOL)flag
- {
- cFlags.xLabelRotate = flag;
- reScale = YES;
- return self;
- }
-
- /* return X axis label rotate state */
- - (BOOL)xLabelRotate
- {
- return cFlags.xLabelRotate;
- }
-
- /* rotate y axis labels */
- - setYLabelRotate:(BOOL)flag
- {
- cFlags.yLabelRotate = flag;
- reScale = YES;
- return self;
- }
-
- /* return Y axis label rotate state */
- - (BOOL)yLabelRotate
- {
- return cFlags.yLabelRotate;
- }
-
- // -------------------------------------------------------------------------------------
- // actions
- // -------------------------------------------------------------------------------------
-
- /* redisplay chart from source */
- - update:sender
- {
- return [self display];
- }
-
- /* override from super to rescale if frame changes */
- - sizeTo:(NXCoord)w :(NXCoord)h
- {
- reScale = YES;
- return [super sizeTo:w :h];
- }
-
- /* add data source */
- - (int)_addData:srcId:(int)sType chart:(int)cType:(float)width:(NXColor)color:(BOOL)isColor
- {
- int count;
- chartData_s src = { sType, srcId, cType, NO, width, isColor, color };
- if (sType == srcTypeNONE) return -1;
- if (!dataList) dataList = [[[Storage alloc] initCount:1 elementSize:sizeof(chartData_s)
- description:(char*)nil] empty];
- count = [dataList count];
- [dataList addElement:&src];
- return count;
- }
-
- /* return source type */
- - (int)_srcType:sourceId
- {
- if ([sourceId isKindOf:[Text class]]) return srcTypeTEXT;
- if ([sourceId isKindOf:[Matrix class]]) return srcTypeMATRIX;
- if ([sourceId isKindOf:[Storage class]]) return srcTypeSTORAGE;
- return srcTypeNONE;
- }
-
- /* add data source (returns list index) (gray) */
- - (int)addDataSource:sourceId type:(int)type lineWidth:(float)width gray:(float)gray
- {
- NXColor color = NXConvertGrayToColor(gray);
- id src = ([sourceId isKindOf:[ScrollView class]])? [sourceId docView] : sourceId;
- return [self _addData:src:[self _srcType:src] chart:type:width:color:NO];
- }
-
- /* add data source (returns list index) (color) */
- - (int)addDataSource:sourceId type:(int)type lineWidth:(float)width color:(NXColor)color
- {
- id src = ([sourceId isKindOf:[ScrollView class]])? [sourceId docView] : sourceId;
- return [self _addData:src:[self _srcType:src] chart:type:width:color:YES];
- }
-
- /* add data stream (gray) */
- - (int)addDataStream:(NXStream*)s type:(int)type lineWidth:(float)width gray:(float)gray
- {
- NXColor color = NXConvertGrayToColor(gray);
- return [self _addData:(id)s:srcTypeSTREAM chart:type:width:color:NO];
- }
-
- /* add data stream (color) */
- - (int)addDataStream:(NXStream*)s type:(int)type lineWidth:(float)width color:(NXColor)color
- {
- return [self _addData:(id)s:srcTypeSTREAM chart:type:width:color:YES];
- }
-
- /* default data source outlet (if added, index will always be zero) */
- - setDataSource:anObject
- {
- int n;
- NXColor color = dftChartColor;
- if (dataList) { [dataList free]; dataList = (id)nil; }
- if (!anObject) return self;
- n = cFlags.drawInColor?
- [self addDataSource:anObject type:dftChartType lineWidth:-1.0 color:color]:
- [self addDataSource:anObject type:dftChartType lineWidth:-1.0 gray:_colorGray(color)];
- if (n >= 0) [self chartDataAt:n]->isEditable = YES;
- return self;
- }
-
- /* set x label matrix */
- - setXLabelMatrix:anObject
- {
- if (![anObject isKindOf:[Matrix class]]) return (id)nil;
- xLabelMatrix = anObject;
- reScale = YES;
- return self;
- }
-
- /* return current x-axis label matrix */
- - xLabelMatrix
- {
- return xLabelMatrix;
- }
-
- /* set y label matrix */
- - setYLabelMatrix:anObject
- {
- if (![anObject isKindOf:[Matrix class]]) return (id)nil;
- yLabelMatrix = anObject;
- reScale = YES;
- return self;
- }
-
- /* return current y-axis label matrix */
- - yLabelMatrix
- {
- return yLabelMatrix;
- }
-
- // -------------------------------------------------------------------------------------
- // data retrieval methods
- // -------------------------------------------------------------------------------------
-
- /* return number of points in data chart set */
- - (int)_pointCount:(chartData_s*)dtaPtr
- {
- if (!dtaPtr) return MIN((floor(dataRange.W) - 1.0), 30.0); // inadequate for xLogScale?
- switch (dtaPtr->_srcType) {
- case srcTypeMATRIX: return [[dtaPtr->_srcId cellList] count];
- case srcTypeSTORAGE: return [dtaPtr->_srcId count];
- }
- return 0;
- }
-
- /* return specific point */
- #define RND(S) (srandom(S*21),(float)(random()&0xFFFF)/(float)0xFFFF)
- - _dataItemAt:(int)index :(chartData_s*)dtaPtr :(float*)x:(float*)y
- {
- id cellId;
- NXPoint *pt;
-
- /* check for sample default chart type */
- if (!dtaPtr) {
- float gx, gy;
- if (!_nibMode) return (id)nil;
- gx = dataRange.X+1.0+(float)(index*(cFlags.xLogScale?rint(dataRange.X+10.0/10.0):1));
- gy = dataRange.Y+dataRange.H*(cFlags.yLogScale?0.15:0.3)*(1.0+RND(index));
- *x = xPLOT(gx);
- *y = yPLOT(gy);
- return self;
- }
-
- /* normal chart type */
- switch (dtaPtr->_srcType) {
- case srcTypeMATRIX: // Matrix
- if (!(cellId = [[dtaPtr->_srcId cellList] objectAt:index])) return (id)nil;
- if (![cellId respondsTo:@selector(floatValue)]) return (id)nil;
- *y = yPLOT([cellId floatValue]);
- *x = xPLOT((float)[cellId tag]);
- return self;
- case srcTypeSTORAGE: // Storage
- pt = (NXPoint*)[dtaPtr->_srcId elementAt:index];
- *x = xPLOT(pt->x);
- *y = yPLOT(pt->y);
- return self;
- }
-
- return (id)nil;
- }
-
- /* build storage object containing points fount in stream (MUST BE EXPLICITLY FREED) */
- - _parseStream:(NXStream*)s :(BOOL)all
- {
- int chartType = dftChartType;
- NXColor chartColor;
- BOOL chartIsColor = NO, newChart;
- chartData_s dtaSrc;
- id dList = (id)nil;
-
- /* reset to beginning */
- NXSeek(s, 0L, NX_FROMSTART);
-
- /* read stream */
- for (newChart = YES; !NXAtEOS(s); ) {
- int v;
- NXPoint pt;
- char *p, b[256];
-
- /* read line */
- for (p = b; !NXAtEOS(s); p++) {
- *p = (char)NXGetc(s);
- if (!*p || (*p == '\n')) break;
- } *p = 0;
-
- /* check for command */
- if ((*b == ':') && !strncasecmp(b, ":chart ", 7)) {
- char typeStr[256];
- float red = -1.0, green = -1.0, blue = -1.0;
- int n, rtn;
-
- /* default chart */
- newChart = YES;
- chartType = dftChartType;
- chartColor = dftChartColor;
- chartIsColor = cFlags.drawInColor;
- if (sscanf(b, "%*s %s %n", typeStr, &n) != 1) continue;
-
- /* change chart type */
- chartType = 0;
- if (index(typeStr, '*')) chartType = dftChartType;
- if (index(typeStr, 'b')) chartType |= chartBAR;
- if (index(typeStr, 'l')) chartType |= chartLINE;
- if (index(typeStr, 'a')) chartType |= chartAREA;
- if (index(typeStr, 'p')) chartType |= chartPOINT;
- if (n > strlen(b)) continue;
-
- /* change color by name */
- if (b[n] == '*') {
- char c[128];
- sscanf(&b[n+1], "%s", c);
- if (!strcasecmp(c,"black" )) {chartColor=NX_COLORBLACK ; chartIsColor=NO ;} else
- if (!strcasecmp(c,"dkgray" )) {chartColor=NX_COLORDKGRAY ; chartIsColor=NO ;} else
- if (!strcasecmp(c,"ltgray" )) {chartColor=NX_COLORLTGRAY ; chartIsColor=NO ;} else
- if (!strcasecmp(c,"white" )) {chartColor=NX_COLORWHITE ; chartIsColor=NO ;} else
- if (!strcasecmp(c,"gray" )) {chartColor=NX_COLORGRAY ; chartIsColor=YES;} else
- if (!strcasecmp(c,"red" )) {chartColor=NX_COLORRED ; chartIsColor=YES;} else
- if (!strcasecmp(c,"orange" )) {chartColor=NX_COLORORANGE ; chartIsColor=YES;} else
- if (!strcasecmp(c,"yellow" )) {chartColor=NX_COLORYELLOW ; chartIsColor=YES;} else
- if (!strcasecmp(c,"green" )) {chartColor=NX_COLORGREEN ; chartIsColor=YES;} else
- if (!strcasecmp(c,"blue" )) {chartColor=NX_COLORBLUE ; chartIsColor=YES;} else
- if (!strcasecmp(c,"purple" )) {chartColor=NX_COLORPURPLE ; chartIsColor=YES;} else
- if (!strcasecmp(c,"cyan" )) {chartColor=NX_COLORCYAN ; chartIsColor=YES;} else
- if (!strcasecmp(c,"brown" )) {chartColor=NX_COLORBROWN ; chartIsColor=YES;} else
- if (!strcasecmp(c,"magenta")) {chartColor=NX_COLORMAGENTA; chartIsColor=YES;} else
- if (!strcasecmp(c,"clear" )) {chartColor=NX_COLORCLEAR ; chartIsColor=YES;}
- continue;
- }
-
- /* change color by number */
- rtn = sscanf(&b[n], "%f %f %f", &red, &green, &blue);
- chartIsColor = (rtn == 3)? YES : NO;
- if (chartIsColor) chartColor = NXConvertRGBToColor(red, green, blue);
- else chartColor = NXConvertGrayToColor(red);
- continue;
- }
-
- /* new chart */
- if (newChart) {
- if (dList && !all) break; // already have the first set
- memset(&dtaSrc, 0, sizeof(chartData_s));
- dtaSrc._srcType = srcTypeSTORAGE;
- dtaSrc._srcId = [[[Storage alloc] initCount:1 elementSize:sizeof(NXPoint)
- description:(char*)nil] empty];
- dtaSrc.chartType = chartType;
- dtaSrc.isEditable = NO;
- dtaSrc.lineWidth = -1.0;
- dtaSrc.isColor = chartIsColor;
- dtaSrc.chartColor = chartColor;
- if (!dList) dList = [[[Storage alloc] initCount:1 elementSize:sizeof(chartData_s)
- description:(char*)nil] empty];
- [dList addElement:&dtaSrc];
- newChart = NO;
- }
-
- /* parse data point */
- v = sscanf(b, "%f %f ", &pt.x, &pt.y);
- if (v <= 0) continue;
- if (v == 1) { pt.y = pt.x; pt.x = (float)[dtaSrc._srcId count]; }
- [dtaSrc._srcId addElement:&pt];
-
- }
-
- /* return parse data list */
- if (dList && ![dList count]) { [dList free]; dList = (id)nil; }
- return dList;
-
- }
-
- // -------------------------------------------------------------------------------------
- // plot chart
- // -------------------------------------------------------------------------------------
-
- /* chart data */
- - _chartData:(chartData_s*)dtaPtr
- {
- BOOL first;
- float x, y, lineWidth;
- int i, cnt = [self _pointCount:dtaPtr], chartType;
-
- /* check for sample chart type */
- if (dtaPtr) {
- if (dtaPtr->isColor) NXSetColor(dtaPtr->chartColor);
- else PSsetgray(_colorGray(dtaPtr->chartColor));
- chartType = dtaPtr->chartType;
- lineWidth = dtaPtr->lineWidth;
- } else {
- _setColor(dftChartColor, cFlags.drawInColor);
- chartType = dftChartType;
- lineWidth = -1.0;
- }
-
- /* area chart */
- if (chartType & chartAREA) {
- PSsetlinewidth((lineWidth>=0.0)?lineWidth:1.0);
- for (first = YES, i = 0; i < cnt ; i++) {
- if (![self _dataItemAt:i :dtaPtr :&x:&y]) break;
- if (first) { PSmoveto(x, axisOrigin.y); first = NO; }
- PSlineto(x, y);
- }
- if (!first) { PSlineto(x, axisOrigin.y); PSclosepath(); PSfill(); }
- }
-
- /* bar chart */
- if (chartType & chartBAR) {
- PSsetlinewidth((lineWidth>=0.0)?xSCALE(lineWidth):xSCALE(0.8));
- for (i = 0; i < cnt ; i++) {
- if (![self _dataItemAt:i :dtaPtr :&x:&y]) break;
- _barALine(x, axisOrigin.y, x, y);
- }
- }
-
- /* line chart */
- if (chartType & chartLINE) {
- PSsetlinewidth((lineWidth>=0.0)?lineWidth:1.0);
- PSnewpath();
- for (first = YES, i = 0; i < cnt ; i++) {
- if (![self _dataItemAt:i :dtaPtr :&x:&y]) break;
- if (first) { PSmoveto(x, y); first = NO; }
- else PSlineto(x, y);
- }
- if (!first) PSstroke();
- }
-
- /* point chart */
- if (chartType & chartPOINT) {
- PSsetlinewidth((lineWidth>=0.0)?lineWidth:1.0);
- for (i = 0; i < cnt ; i++) {
- if (![self _dataItemAt:i :dtaPtr :&x:&y]) break;
- _barDrawPoint(x, y, 3.0);
- }
- }
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // draw chart view
- // -------------------------------------------------------------------------------------
-
- /* calculate printed decimal precision */
- static int _dPrecision(float num)
- {
- int p;
- char floatStr[32], *fPtr;
- if (num > 9999.0) return 0;
- sprintf(floatStr, "%f", num);
- for (fPtr = floatStr+strlen(floatStr)-1; (*fPtr=='0') || (*fPtr==' ');) *(fPtr--) = 0;
- for (fPtr = floatStr; *fPtr && (*fPtr != '.'); fPtr++);
- p = (*fPtr)? strlen(fPtr + 1) : 0;
- return p;
- }
-
- /* rescale axis */
- - _rescaleAxis
- {
- char label[64];
- NXSize xLS, yLS;
- float fontHeight = [currentFont pointSize] * 0.76;
-
- /* save upper range limit */
- maxRange.x = dataRange.X + dataRange.W;
- maxRange.y = dataRange.Y + dataRange.H;
-
- /* check number of tick marks */
- if (!cFlags.xLogScale && (dataRange.W / tickMark.W >= 250.0))
- tickMark.W = dataRange.W / 250.0;
- if (!cFlags.yLogScale && (dataRange.H / tickMark.H >= 250.0))
- tickMark.H = dataRange.H / 250.0;
-
- /* calc tick mark length and line width */
- tickLength = MAX(MIN(fontHeight, 12.0), 5.0);
- tickWidth = ([currentFont pointSize] >= 20.0)? 2.0 : 1.0;
-
- /* label precision */
- precisionX = _dPrecision(tickMark.W);
- precisionY = _dPrecision(tickMark.H);
-
- /* label size */
- if (xLabelMatrix) [xLabelMatrix getCellSize:&xLblSize];
- else {
- sprintf(label, "%.*f", precisionX, maxRange.x);
- xLblSize.width = fontWIDTH(label);
- xLblSize.height = fontHeight;
- }
- if (!cFlags.xLabelRotate) xLS = xLblSize;
- else { xLS.width = xLblSize.height; xLS.height = xLblSize.width; }
- if (yLabelMatrix) [yLabelMatrix getCellSize:&yLblSize];
- else {
- sprintf(label, "%.*f", precisionY, maxRange.y);
- yLblSize.width = fontWIDTH(label);
- yLblSize.height = fontHeight;
- }
- if (!cFlags.yLabelRotate) yLS = yLblSize;
- else { yLS.width = yLblSize.height; yLS.height = yLblSize.width; }
-
- /* calculate chart frame */
- chartFrame.X = (cFlags.yShowLabels?yLS.width :0.0) + tickLength / 2.0 + 2.0;
- if (chartFrame.X < xLS.width / 2.0) chartFrame.X = xLS.width / 2.0;
- chartFrame.Y = (cFlags.xShowLabels?xLS.height:0.0) + tickLength / 2.0 + 2.0;
- chartFrame.W = bounds.W - chartFrame.X - (cFlags.xShowLabels?xLS.width *0.50:2.0);
- chartFrame.H = bounds.H - chartFrame.Y - (cFlags.yShowLabels?yLS.height :2.0);
-
- /* scale factor */
- dataScale.x = dataRange.W? chartFrame.W/(xPOINT(maxRange.x)-xPOINT(dataRange.X)) : 0.0;
- dataScale.y = dataRange.H? chartFrame.H/(yPOINT(maxRange.y)-yPOINT(dataRange.Y)) : 0.0;
-
- /* axis origin */
- axisOrigin.x = chartFrame.X - xSCALE(dataRange.X);
- axisOrigin.y = chartFrame.Y - ySCALE(dataRange.Y);
-
- /* actual displayed axis position */
- axisPosition.x = xPLOT((maxRange.x > 0.0)? MAX(dataRange.X, 0.0): MIN(maxRange.x, 0.0));
- axisPosition.y = yPLOT((maxRange.y > 0.0)? MAX(dataRange.Y, 0.0): MIN(maxRange.y, 0.0));
-
- return self;
- }
-
- /* draw Y grid lines (parallel X-axis) */
- - _drawYGridLines
- {
-
- if (cFlags.yGridDraw && (tickMark.H > 0.0)) {
- float t = tickMark.H, y = t * (long)(dataRange.Y / t), p;
- if (y < dataRange.Y) y += t;
- _setColor(yGridColor, cFlags.drawInColor);
- PSsetlinewidth(tickWidth);
- for (p = y; p <= maxRange.y; p += t) {
- _barRLine(chartFrame.X, yPLOT(p), chartFrame.W, 0.0);
- if (cFlags.yLogScale) while (p / t >= 10.0) t *= 10.0;
- }
- }
-
- return self;
- }
-
- /* draw X grid lines (parallel Y-axis) */
- - _drawXGridLines
- {
-
- if (cFlags.xGridDraw && (tickMark.W > 0.0)) {
- float t = tickMark.W, x = t * (long)(dataRange.X / t), p;
- if (x < dataRange.X) x += t;
- _setColor(xGridColor, cFlags.drawInColor);
- PSsetlinewidth(tickWidth);
- for (p = x; p <= maxRange.x; p += t) {
- _barRLine(xPLOT(p), chartFrame.Y, 0.0, chartFrame.H);
- if (cFlags.xLogScale) while (p / t >= 10.0) t *= 10.0;
- }
- }
-
- return self;
- }
-
- /* draw Y axis */
- - _drawYAxis
- {
-
- PSsetlinewidth(tickWidth);
- _setColor(axisColor, cFlags.drawInColor);
- _barRLine(axisPosition.x, chartFrame.Y, 0.0, chartFrame.H);
-
- return self;
- }
-
- /* draw X axis */
- - _drawXAxis
- {
-
- PSsetlinewidth(tickWidth);
- _setColor(axisColor, cFlags.drawInColor);
- _barRLine(chartFrame.X, axisPosition.y, chartFrame.W, 0.0);
-
- return self;
- }
-
- /* draw Y-axis label */
- - _drawYLabel:(int)i:(float)p:(NXPoint*)point
- {
- char label[64];
- NXRect r;
- BOOL stateSaved = NO;
-
- /* label size */
- if (yLabelMatrix) {
- *label = 0;
- r.W = yLblSize.width;
- r.H = yLblSize.height;
- } else {
- sprintf(label, "%.*f", precisionY, p);
- r.W = fontWIDTH(label);
- r.H = yLblSize.height / 4.0;
- }
-
- /* label orientation */
- if (cFlags.yLabelRotate) {
- PSgsave(); stateSaved = YES;
- PStranslate(point->x - 1.0, MAX(point->y - r.H / 2.0, 0.0));
- PSrotate(90.0);
- r.X = 0.0;
- r.Y = 0.0;
- } else {
- r.X = point->x - r.W - 1.0;
- r.Y = MAX(point->y - r.H / 2.0, 0.0);
- }
-
- /* draw label */
- if (yLabelMatrix) {
- id cellId = [yLabelMatrix cellAt:i:0];
- if (!cellId) cellId = [yLabelMatrix cellAt:0:i];
- if (cellId) [cellId drawSelf:&r inView:self];
- } else _barDrawText(r.X, r.Y, label);
-
- /* restore state */
- if (stateSaved) PSgrestore();
-
- return self;
- }
-
- /* draw Y-axis labels */
- - _drawYAxisLabels
- {
-
- if (tickMark.H > 0.0) {
- NXPoint point;
- int i;
- float t = tickMark.H, y = t * (long)(dataRange.Y / t), p;
- point.x = axisPosition.x - floor(tickLength / 2.0);
- if (y < dataRange.Y) y += t;
- if (!yLabelMatrix && cFlags.yShowLabels) [currentFont set];
- for (i = 0, p = y; p <= maxRange.y; p += t) {
-
- /* draw tickMark */
- point.y = yPLOT(p);
- _setColor(axisColor, cFlags.drawInColor);
- PSsetlinewidth(tickWidth);
- _barRLine(point.x + 1.0, point.y, tickLength, 0.0);
- if (!cFlags.yShowLabels) {
- if (cFlags.yLogScale) while (p / t >= 10.0) t *= 10.0;
- continue;
- }
-
- /* draw label */
- if (!cFlags.yLogScale) {
- [self _drawYLabel:i++:p:&point];
- } else
- if (p / t >= 10.0) {
- [self _drawYLabel:i++:p:&point];
- while (p / t >= 10.0) t *= 10.0;
- }
-
- }
- }
-
- return self;
- }
-
- /* draw X-axis label */
- - _drawXLabel:(int)i:(float)p:(NXPoint*)point
- {
- char label[64];
- NXRect r;
- BOOL stateSaved = NO;
-
- /* label size */
- if (xLabelMatrix) {
- *label = 0;
- r.W = xLblSize.width;
- r.H = xLblSize.height;
- } else {
- sprintf(label, "%.*f", precisionX, p);
- r.W = fontWIDTH(label);
- r.H = xLblSize.height;
- }
-
- /* label orientation */
- if (cFlags.xLabelRotate) {
- PSgsave(); stateSaved = YES;
- PStranslate(point->x + r.H / 2.0, MAX(point->y - r.W - 1.0, 0.0));
- PSrotate(90.0);
- r.X = 0.0;
- r.Y = 0.0;
- } else {
- r.X = point->x - r.W / 2.0;
- r.Y = MAX(point->y - r.H - 1.0, 0.0);
- }
-
- /* draw label */
- if (xLabelMatrix) {
- id cellId = [xLabelMatrix cellAt:0:i];
- if (!cellId) cellId = [xLabelMatrix cellAt:i:0];
- if (cellId) [cellId drawSelf:&r inView:self];
- } else _barDrawText(r.X, r.Y, label);
-
- /* restore state */
- if (stateSaved) PSgrestore();
-
- return self;
- }
-
- /* draw X-axis labels */
- - _drawXAxisLabels
- {
-
- if (tickMark.W > 0.0) {
- NXPoint point;
- int i;
- float t = tickMark.W, x = t * (long)(dataRange.X / t), p;
- point.y = axisPosition.y - floor(tickLength / 2.0);
- if (x < dataRange.X) x += t;
- if (!xLabelMatrix && cFlags.xShowLabels) [currentFont set];
- for (i = 0, p = x; p <= maxRange.x; p += t) {
-
- /* draw tickMark */
- point.x = xPLOT(p);
- _setColor(axisColor, cFlags.drawInColor);
- PSsetlinewidth(tickWidth);
- _barRLine(point.x, point.y + 1.0, 0.0, tickLength);
- if (!cFlags.xShowLabels) {
- if (cFlags.xLogScale) while (p / t >= 10.0) t *= 10.0;
- continue;
- }
-
- /* draw label */
- if (!cFlags.xLogScale) {
- [self _drawXLabel:i++:p:&point];
- } else
- if (p / t >= 10.0) {
- [self _drawXLabel:i++:p:&point];
- while (p / t >= 10.0) t *= 10.0;
- }
-
- }
- }
-
- return self;
- }
-
- /* draw data */
- - _drawChartData
- {
-
- PSgsave();
- _barClipBox(chartFrame.X, chartFrame.Y, chartFrame.W, chartFrame.H);
- if (dataList) {
- int s;
- for (s = 0; s < [dataList count]; s++) {
- chartData_s *dtaPtr = (chartData_s*)[dataList elementAt:s];
- if ((dtaPtr->_srcType == srcTypeTEXT) || (dtaPtr->_srcType == srcTypeSTREAM)) {
- id list, src = dtaPtr->_srcId;
- NXStream *s = (dtaPtr->_srcType==srcTypeTEXT)?[src stream]:(NXStream*)src;
- if (list = [self _parseStream:s:YES]) {
- int i, cnt = [list count];
- for (i = 0; i < cnt; i++) {
- chartData_s *dp = (chartData_s*)[list elementAt:i];
- dp->lineWidth = dtaPtr->lineWidth;
- [self _chartData:dp];
- [dp->_srcId free];
- }
- [list free];
- }
- } else [self _chartData:dtaPtr];
- }
- } else [self _chartData:(chartData_s*)nil];
- PSgrestore(); // remove clip path
-
- return self;
- }
-
- /* draw all chart types */
- // this method has been arranged to provide for easy subclassing
- - _drawChart
- {
-
- [self _drawYGridLines]; // Y axis grid lines
- [self _drawXGridLines]; // X axis grid lines
- [self _drawChartData]; // chart data
- [self _drawYAxis]; // Y axis
- [self _drawXAxis]; // X axis
- [self _drawYAxisLabels]; // Y axis labels
- [self _drawXAxisLabels]; // X axis labels
-
- return self;
- }
-
- /* draw self */
- - drawSelf:(const NXRect *)r :(int)n
- {
-
- /* fill background */
- if (!cFlags.transparent) {
- _setColor(backgroundColor, cFlags.drawInColor);
- NXRectFill(&bounds);
- }
-
- /* rescale if necessary */
- if (reScale) { [self _rescaleAxis]; reScale = NO; }
-
- /* draw */
- [self _drawChart];
-
- /* highlight if first responder */
- //if (isFirstResponder) NXHighlightRect(&bounds);
-
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // object archiving stuff
-
- - read:(NXTypedStream*)s
- {
- int ver;
- [super read:s];
- NXReadTypes(s, "i",&ver);
- NXReadRect (s, &dataRange);
- NXReadRect (s, &tickMark);
- NXReadTypes(s, "@is",¤tFont,&dftChartType,&cFlags);
- if (ver <= 1) { float dmy; NXReadTypes(s, "fffff",&dmy,&dmy,&dmy,&dmy,&dmy); }
- backgroundColor = NXReadColor(s);
- dftChartColor = NXReadColor(s);
- axisColor = NXReadColor(s);
- xGridColor = NXReadColor(s);
- yGridColor = NXReadColor(s);
- delegate = self;
- xLabelMatrix = (id)nil;
- yLabelMatrix = (id)nil;
- actionMatrix = (id)nil;
- dataList = (id)nil;
- reScale = YES;
- return self;
- }
-
- - write:(NXTypedStream*)s
- {
- int ver = [[self class] version];
- [super write:s];
- NXWriteTypes(s, "i",&ver);
- NXWriteRect (s, &dataRange);
- NXWriteRect (s, &tickMark);
- NXWriteTypes(s, "@is",¤tFont,&dftChartType,&cFlags);
- NXWriteColor(s, backgroundColor);
- NXWriteColor(s, dftChartColor);
- NXWriteColor(s, axisColor);
- NXWriteColor(s, xGridColor);
- NXWriteColor(s, yGridColor);
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // firstResponder for getting copy/paste commands
-
- /* allow becoming first responder */
- - (BOOL)acceptsFirstResponder
- {
- return YES;
- }
-
- /* become first responder */
- - becomeFirstResponder
- {
- isFirstResponder = YES;
- [self display];
- return self;
- }
-
- /* resign first responder */
- - resignFirstResponder
- {
- isFirstResponder = NO;
- [self display];
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // mouseDown hit detection
-
- /* handle point selected */
- - _selectedPoint:(chartData_s*)dtaPtr:(NXPoint*)point:(int)index clicks:(int)clicks
- {
- if (clicks == 2) {
- if (delegate && [delegate respondsTo:@selector(chartDataSelected:index:)]) {
- [delegate chartDataSelected:dtaPtr index:index];
- }
- }
- return self;
- }
-
- /* chart data */
- - (BOOL)_hit:(NXPoint*)pt data:(chartData_s*)dtaPtr hit:(NXPoint*)hit:(int*)index
- {
- NXPoint w, h;
- int i, cnt = [self _pointCount:dtaPtr];
-
- /* valid range */
- if (dtaPtr->chartType & chartBAR) { w.x = xSCALE(0.8) / 2.0; w.y = 0; }
- else { w.x = 3.0; w.y = w.x; }
-
- /* check hit */
- for (i = 0; i < cnt; i++) {
- if (![self _dataItemAt:i :dtaPtr :&h.x:&h.y]) break;
- if ((pt->x >= h.x - w.x) && (pt->x <= h.x + w.x) &&
- (((dtaPtr->chartType & chartBAR) && (pt->y <= h.y + 8.0)) ||
- ((pt->y >= h.y - w.y) && (pt->y <= h.y + w.y)) ) ) {
- *index = i;
- *hit = h;
- return YES;
- }
- }
-
- /* no hit */
- return NO;
-
- }
-
- /* data point hit detection */
- - _dataHitDetection:(NXPoint*)pt clicks:(int)clicks
- {
- NXPoint hPt;
- int s, index = -1;
-
- /* ignore if no data points */
- if (!dataList) return self;
-
- /*loop through datasets to find hit */
- for (s = [dataList count]; s;) {
- BOOL hit = NO;
- chartData_s *dtaPtr = (chartData_s*)[dataList elementAt:--s];
- if ((dtaPtr->_srcType == srcTypeTEXT) || (dtaPtr->_srcType == srcTypeSTREAM)) {
- id list, src = dtaPtr->_srcId;
- NXStream *s = (dtaPtr->_srcType == srcTypeTEXT)? [src stream] : (NXStream*)src;
- if (list = [self _parseStream:s:NO]) {
- int i;
- hit = [self _hit:pt data:(chartData_s*)[list elementAt:0] hit:&hPt:&index];
- for (i=0;i<[list count];i++) [((chartData_s*)[list elementAt:i])->_srcId free];
- [list free];
- }
- } else hit = [self _hit:pt data:dtaPtr hit:&hPt:&index];
- if (hit) { [self _selectedPoint:dtaPtr:&hPt:index clicks:clicks]; break; }
- }
-
- return self;
- }
-
- /* intercept mouse down */
- - mouseDown:(NXEvent*)e
- {
- NXPoint pt = e->location;
- [self convertPoint:&pt fromView:(id)nil];
- if (![self mouse:&pt inRect:&chartFrame]) return [super mouseDown:e];
- if (e->data.mouse.click == 2) [self _dataHitDetection:&pt clicks:2];
- return [super mouseDown:e];
- }
-
- // -------------------------------------------------------------------------------------
- // chart data point change/selection delegate methods
-
- /* data point selected */
- - chartDataSelected:(chartData_s*)dtaPtr index:(int)index
- {
- id c, m = actionMatrix, srcId = dtaPtr->_srcId;
- if (!m && [srcId isKindOf:[Matrix class]] && ([srcId target] != self)) m = srcId;
- if (m && (c = [[m cellList] objectAt:index])) { [m selectCell:c]; [m sendAction]; }
- return self;
- }
-
- @end
-