This is ChartOfMatrix.m in view mode; [Download] [Up]
/* * BarChart, A simple multi source loadable class. * * Written by: Joe Freeman 7/92 * * HSB color sweep stolen from some of Randy Nelson's code * should use NXEqualColor() for duplicates * */ #import "ChartOfMatrix.h" #import <dpsclient/psops.h> #import <c.h> #import <stdio.h> #define NUM_BOGUS 8 /* number of bars to draw when no data */ #define COM_VERSION 3.3 /* version 3 ported to 3.0 */ @implementation ChartOfMatrix /*============================================================ *factory *============================================================*/ + initialize { [super initialize]; [ChartOfMatrix setVersion:2]; return self; } - (const char *)getInspectorClassName { return "ChartOfMatrixInspector"; } - initFrame:(NXRect *)r { self = [super initFrame:r]; minSheetSet = 0.0; maxSheetSet = 1.0; COM_Flags.autoScale = YES; COM_Flags.drawType = DRAW_V_BAR; backgroundColor = NX_COLORWHITE; highlightColor = NX_COLORBLACK; highlightIndex = MAXINT; /* don't show on default */ COM_Flags.drawFrame = YES; hMargin = vMargin = 15.0; hMargin = vMargin = 5.0; numPrototypes = 5; COM_Flags.randomBarColors = YES; return self; } - awake { [super awake]; highlightIndex = MAXINT; [self registerForDraggedTypes:&NXColorPboardType count:1]; return self; } /*============================================================ * color dragging support *============================================================*/ - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender { if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) { return NX_DragOperationGeneric; } else { return NX_DragOperationNone; } } - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender { NXPoint p = [sender draggingLocation]; NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]); [self setBackgroundColor: c]; return YES; } /*============================================================ * instance set / query methods *============================================================*/ - setDataSrc:anObject { dataSrc = anObject; return self; } - setGraphType:(int)drawCode { COM_Flags.drawType = drawCode; [self update]; return self; } - (int)graphType { return COM_Flags.drawType; } - takeRandomColorStateFrom:sender { [self setRandomBarColorEnabled:[sender state]]; return self; } - setRandomBarColorEnabled:(BOOL)flag { COM_Flags.randomBarColors = flag; [self update]; return self; } - (BOOL)isRandomBarColorEnabled { return COM_Flags.randomBarColors; } - takeBackgroundColorFrom:sender { [self setBackgroundColor: [sender color]]; return self; } - setBackgroundColor:(NXColor)aColor { backgroundColor = aColor; [self update]; return self; } - (NXColor)backgroundColor { return backgroundColor; } - takeHighlightColorFrom:sender { [self setHighlightColor: [sender color]]; return self; } - setHighlightColor:(NXColor)aColor { highlightColor = aColor; [self update]; return self; } - (NXColor)highlightColor { return highlightColor; } /* set and query the size for the margins (in points) */ - takeHMarginFrom:sender { hMargin = [sender floatValue]; [self update]; return self; } - takeVMarginFrom:sender { vMargin = [sender floatValue]; [self update]; return self; } - (float)hMargin { return hMargin; } - (float)vMargin { return vMargin; } - takeAutoScaleStateFrom:sender { COM_Flags.autoScale = [sender floatValue]; return self; } - setAutoScale:(BOOL)flag { COM_Flags.autoScale = flag; [self update]; return self; } - (BOOL)autoScale { return COM_Flags.autoScale; } /* fancy controls */ - takeFrameStateFrom:sender { COM_Flags.drawFrame = [sender state]; [self update]; return self; } - (BOOL)frameState { return COM_Flags.drawFrame; } - takeNumProtosFrom:sender { numPrototypes= [sender intValue]; [self update]; return self; } - (int)numProtos { return numPrototypes; } - takeBorderTypeFrom:sender { borderType = [sender tag]; [self update]; return self; } - (int)borderType { return borderType; } /* when autoscale is off, set the min and max for the sheet */ - takeMinValueFrom:sender { minSheetSet = [sender floatValue]; return self; } - takeMaxValueFrom:sender { maxSheetSet = [sender floatValue]; return self; } - (double)minValue { return minSheetSet; } - (double)maxValue { return maxSheetSet; } /*============================================================ * target/action *============================================================*/ - (BOOL)acceptsFirstResponder { return YES; } - copy:sender { id pb = [Pasteboard new]; /* global pasteboard object */ NXStream *st; /* stream to collect data in */ char *data; /* actual data buffer */ int length; /* length of data */ int maxLength; /* (not used here) */ /* declare that we will supply a single type of data, eps */ [pb declareTypes:&NXPostScriptPboard num:1 owner:self]; /* get a stream which writes to memory */ st = NXOpenMemory (NULL, 0, NX_WRITEONLY); /* find bounding box and then write it to the stream */ [self copyPSCodeInside:&bounds to:st]; /* get actuall data buffer form stream */ NXGetMemoryBuffer (st, &data, &length, &maxLength); /* write data to pasteboard */ [pb writeType:NXPostScriptPboard data:data length:length ]; /* dealocate stream including it's buffer */ NXCloseMemory (st, NX_FREEBUFFER ); return self; } /*============================================================ * do real work *============================================================*/ - (int)numLocations { int numRows,numCols; if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){ return [dataSrc count]; } else if ([dataSrc respondsTo:@selector(selectedCell)]){ [dataSrc getNumRows:&numRows numCols:&numCols]; return MAX(numRows,numCols); } else if (!dataSrc) { /* put up some dummy graph on palette */ return NUM_BOGUS; } return 0; } - (float)valueOfLocation:(int)n { static float data[NUM_BOGUS] = {.1, .3, .2, .7, .4, .8, .5, .98}; int numRows,numCols; float theValue; id mrValue = [[DBValue alloc] init]; if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){ [dataSrc getValue:mrValue forProperty:mrExpression at:n]; theValue = [mrValue floatValue]; [mrValue free]; return theValue; } else if ([dataSrc respondsTo:@selector(selectedCell)]){ [dataSrc getNumRows:&numRows numCols:&numCols]; if (numCols == 1) { return [[dataSrc cellAt:n :0] floatValue]; } else { /* numRows == 1 */ return [[dataSrc cellAt:0 :n] floatValue]; } } else if (!dataSrc) { return data[n]; } return 0.0; } /*============================================================ * target action loading *============================================================*/ - plotFromMatrix:sender { if (!dataSrc && [sender respondsTo:@selector(selectedCell)]){ [self setDataSrc:sender]; } [self update]; return self; } /*============================================================ * dbKit support *============================================================*/ - associationContentsDidChange:association { mrFetchGroup = [association fetchGroup]; dataSrc = [mrFetchGroup recordList]; highlightIndex = [mrFetchGroup currentRecord]; mrExpression = [association expression]; [self update]; return self; } /* ** adh 7/27/92 ** Do this so we redraw when values are updated in the UI */ - association:association setValue:(DBValue *)value { return [self update]; } - associationSelectionDidChange:association { /* assume fetchgroup doesn't change so don't update mrFetchGroup */ highlightIndex = [[association fetchGroup] currentRecord]; return [self update]; } - associationCurrentRecordDidDelete:association { /* assumes record list is the same */ return [self update]; return self; } /*============================================================ * do a selection with the mouse *============================================================*/ - mouseDown:(NXEvent *)theEvent { NXEvent lastEvent; NXRect rectOfBar; NXRect drawRect; int longSize = [self numLocations]; int index; if ((COM_Flags.drawType != DRAW_H_BAR && COM_Flags.drawType != DRAW_V_BAR ) || ![dataSrc respondsTo:@selector(getValue:forProperty:at:)]) { NXBeep(); return nil; } lastEvent = *theEvent; [self convertPoint: &lastEvent.location fromView:nil]; /* modify the mouse position to be in psuedo drawing area coords */ lastEvent.location.x -= hMargin; lastEvent.location.y -= vMargin; /* allow for the margins when calc'ing the bar moused down on */ drawRect = bounds; drawRect.origin.x += hMargin; drawRect.origin.y += vMargin; drawRect.size.width -= (2*hMargin); drawRect.size.height -= (2*vMargin); for ( index = 0 ; index < longSize; index++){ if (COM_Flags.drawType == DRAW_V_BAR) { [self calcRect:&rectOfBar ofBar:index insideRect:&drawRect usingMin:NX_Y(&bounds) ]; rectOfBar.origin.y = bounds.origin.y; rectOfBar.size.height = bounds.size.height; } else if (COM_Flags.drawType == DRAW_H_BAR) { [self calcRect:&rectOfBar ofBar:index insideRect:&drawRect usingMin:NX_X(&bounds) ]; rectOfBar.origin.x = bounds.origin.x; rectOfBar.size.width = bounds.size.width; } else { return nil; } if ([self mouse:&lastEvent.location inRect:&rectOfBar]){ [mrFetchGroup setCurrentRecord:index]; break; } } return self; } /*============================================================ * target/action *============================================================*/ - read:(NXTypedStream *)stream { int tmpScale, tmpDrawFrame, tmpDrawType; [super read:stream]; dataSrc = NXReadObject(stream); minField = NXReadObject(stream); maxField = NXReadObject(stream); meanField = NXReadObject(stream); /* the first demo palette went out as version 1 */ if (NXTypedStreamClassVersion(stream, "ChartOfMatrix")<2) { NXReadTypes(stream,"ffffiii", &minSheetSet,&maxSheetSet, &vMargin,&hMargin, &tmpScale, &tmpDrawType, &tmpDrawFrame); COM_Flags.autoScale = tmpScale; COM_Flags.drawType = tmpDrawType; COM_Flags.drawFrame = tmpDrawFrame; COM_Flags.randomBarColors = YES; } else { NXReadTypes(stream,"ffffi", &minSheetSet,&maxSheetSet, &vMargin,&hMargin, &COM_Flags); } backgroundColor = NXReadColor(stream); highlightColor = NXReadColor(stream); NXReadTypes(stream,"ii", &numPrototypes, &borderType); return self; } - write:(NXTypedStream *)stream { [super write:stream]; NXWriteObjectReference(stream, dataSrc); NXWriteObjectReference(stream, minField); NXWriteObjectReference(stream, maxField); NXWriteObjectReference(stream, meanField); NXWriteTypes(stream,"ffffi", &minSheetSet,&maxSheetSet, &vMargin,&hMargin, &COM_Flags); NXWriteColor(stream, backgroundColor); NXWriteColor(stream, highlightColor); NXWriteTypes(stream,"ii", &numPrototypes, &borderType); return self; } /*============================================================ *display *============================================================*/ - calcMin:(float *)rMin andMax:(float *)rMax andMean:(float *)rMean; { int index; int longSize; float thisVal; float sumAll = 0; *rMin = *rMax = [self valueOfLocation:0]; longSize = [self numLocations]; /* first figure out what the maxs and mins are */ for ( index = 0 ; index < longSize; index++){ thisVal = [self valueOfLocation:index]; if (*rMin > thisVal) *rMin = thisVal; if (*rMax < thisVal) *rMax = thisVal; sumAll += thisVal; } *rMean = sumAll / longSize; [minField setFloatValue:*rMin]; [maxField setFloatValue:*rMax]; [meanField setFloatValue:*rMean]; return self; } - renderVLines:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal { int longSize; /* numRows or numCols, whichever is the long side */ int index; float thisVal; float cellWidth; /* the width of a unit (N) to plot */ /* scale the plot */ PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) ); PStranslate(0.0, -minSheetVal); longSize = [self numLocations]; cellWidth = r->size.width / (longSize); /* now plot each square */ PSsetgray(NX_BLACK); thisVal = [self valueOfLocation:0]; PSmoveto(cellWidth/2.0, thisVal); for ( index = 0 ; index < longSize; index++){ thisVal = [self valueOfLocation:index]; PSlineto(cellWidth*index+ cellWidth/2.0, thisVal); } PSstroke(); return self; } /* we can position the bars anywhere inside the view by changing the * insideRect parameter */ - calcRect:(NXRect *)r ofBar:(int)n insideRect:(NXRect *)boundingRect usingMin:(float)minSheetVal { float thisVal; float cellWidth; /* width of a unit (N) to plot */ thisVal = [self valueOfLocation:n]; if (COM_Flags.drawType == DRAW_V_BAR){ cellWidth = boundingRect->size.width / (3 * [self numLocations] + 1); r->origin.x = n * 3 * cellWidth + cellWidth; if (thisVal < 0.0){ r->origin.y = thisVal; r->size.height = -thisVal; }else{ r->origin.y = MAX(minSheetVal, 0.0); r->size.height= thisVal-r->origin.y; } r->size.width = 2 * cellWidth; } else /* assume h bar */ { cellWidth = boundingRect->size.height / (3 * [self numLocations] + 1); r->origin.y = n * 3 * cellWidth + cellWidth; if (thisVal < 0.0){ r->origin.x = thisVal; r->size.width = -thisVal; }else{ r->origin.x = MAX(minSheetVal, 0.0); r->size.width= thisVal - r->origin.x; } r->size.height = 2 * cellWidth; } return self; } /* this should probably be empty but because Kris asked for a bar/lines switch * in the inspectorwe have the abillity to draw bars (vertical). Of course * this makes the vertical bar drawing object almost codeless */ - renderBars:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal { int longSize; /* numRows/numCols, whichever is long side */ int index; NXRect barRect; /* bounding rectangle for bar in the chart */ NXColor HSBColor; /* in case randomBarColors */ longSize = [self numLocations]; if (COM_Flags.drawType == DRAW_V_BAR){ /* scale the plot */ PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) ); PStranslate(0.0, -minSheetVal); } else /* assume h bar */ { /* scale the plot */ PSscale(r->size.width / ( maxSheetVal - minSheetVal), 1.0 ); PStranslate( -minSheetVal, 0.0); } /* now plot each square */ for ( index = 0 ; index < longSize; index++){ if (index == highlightIndex) NXSetColor(highlightColor); else if (!COM_Flags.randomBarColors) { PSsetgray ((1.0 / (longSize+2.0)) * (index +1)); } else { HSBColor = NXConvertHSBToColor( ((float)index / (float)longSize), 1.0, 1.0); NXSetColor(HSBColor); } [self calcRect:&barRect ofBar:index insideRect:(NXRect *)r usingMin:minSheetVal]; NXRectFill(&barRect); } return self; } /* spacing: Each plot is a width of 2N and each gap is a width of 1N * total width is m*(2N+N) + N = 3mN+N = where m = number of bars * Thus N = width / (3m+1) * * scaling: The scaling can make some stuff look pretty funny. */ - drawSelf:(NXRect *)r :(int)c { float minCellVal, maxCellVal; /* min & max of values to be plotted */ float meanCellVal; /* mean of the plotted values */ float minSheetVal,maxSheetVal;/* RangeOfNumbers will plot in graph */ NXRect rectOfPlot; /* bounds of rect that will hold plot */ NXSetColor(backgroundColor); NXRectFill(&bounds); PSsetgray(NX_BLACK); switch (borderType) { case NX_LINE: NXFrameRect(&bounds); break; case NX_BEZEL: NXDrawWhiteBezel(&bounds,&bounds); break; case NX_GROOVE: NXDrawGroove(&bounds, &bounds); break; default: break; } rectOfPlot= bounds; rectOfPlot.origin.x += hMargin; rectOfPlot.origin.y += vMargin; rectOfPlot.size.width -=2*hMargin; rectOfPlot.size.height -=2*vMargin; if ( NX_WIDTH(&bounds) < (2*hMargin) || NX_HEIGHT(&bounds) < (2*vMargin) ) return self; [window disableFlushWindow]; PSgsave(); PSsetlinewidth(0.0); PStranslate (hMargin, vMargin); if (YES){ /* great we only have a single axis to work on */ [self calcMin:&minCellVal andMax:&maxCellVal andMean:&meanCellVal]; if (COM_Flags.autoScale){ /* give us a plot if they are all the same (but not 0.0) */ if (minCellVal == maxCellVal){ if (minCellVal > 0.0) minCellVal = 0.0; else if (maxCellVal < 0.0) maxCellVal = 0.0; } /* figure out what min and the max should be on the sheet */ if (minCellVal == 0.0) minSheetVal= 0.0; else minSheetVal= minCellVal - ((maxCellVal - minCellVal)*0.2); /* we shouldn't have pushed this across the origin */ if (minSheetVal < 0.0 && minCellVal > 0.0) minSheetVal = 0.0; if (maxCellVal == 0.0) maxSheetVal = 0.0; else maxSheetVal= maxCellVal +((maxCellVal - minCellVal)* 0.2); /* make sure we didn't go across the origin */ if (maxSheetVal > 0.0 && maxCellVal < 0.0) maxSheetVal = 0.0; } else { minSheetVal = minSheetSet; maxSheetVal = maxSheetSet; } switch(COM_Flags.drawType){ case DRAW_H_BAR: case DRAW_V_BAR: [self renderBars:&rectOfPlot min:minSheetVal max:maxSheetVal]; break; case DRAW_H_LINE: break; case DRAW_V_LINE: [self renderVLines:&rectOfPlot min:minSheetVal max:maxSheetVal ]; break; default: break; } PSgrestore(); if (COM_Flags.drawFrame){ PSsetgray(NX_BLACK); NXFrameRect(&rectOfPlot); } } [window reenableFlushWindow]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.