Reputation: 670
I want to program a plot that scrolls automatically, so the actual time is on the right side. (marked red)
Data is added irregular with a timeinterval. How can i achieve this? I already have a scrolling graph (based on the example code provided by coreplot). But it's not completely accurate and quite cpu consuming. Also the labels should be "round" times like 14:52:00 14:52:20 and so on.
This is my code so far:
TestWindowController.h
@interface TestWindowController : NSWindowController<CPTPlotDataSource> {
NSMutableArray *plotData;
double currentIndex;
NSDate *referenceDate;
NSTimer *scrollTimer;
NSTimer *newDataTimer;
}
@property (weak) IBOutlet CPTGraphHostingView *graphView;
TestWindowController.m
NSString *kPlotIdentifier = @"Data Source Plot";
const double kTimePeriod = 60.0; // 1 Minute
const double kFrameRate = 10.0; // frames per second
const double kAlpha = 0.25; // smoothing constant
@interface DataObj : NSObject {
NSTimeInterval timeInterval;
double testValue;
}
@property(readwrite) NSTimeInterval timeInterval;
@property(readwrite) double testValue;
@end
@implementation DataObj
@synthesize timeInterval;
@synthesize testValue;
@end
@implementation TestWindowController
@synthesize graphView;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
// Set a reference Date
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit)
fromDate:[NSDate date]];
[dateComponents setSecond:0];
referenceDate = [gregorian dateFromComponents:dateComponents];
currentIndex = 0.0;
// Core Plot
CPTGraph *graph = [[CPTXYGraph alloc] initWithFrame:CGRectZero];
graphView.hostedGraph = graph;
[graph applyTheme:[CPTTheme themeNamed:kCPTPlainBlackTheme]];
//[self setTitleDefaultsForGraph:graph withBounds:bounds];
//[self setPaddingDefaultsForGraph:graph withBounds:bounds];
graph.plotAreaFrame.paddingTop = 15.0;
graph.plotAreaFrame.paddingRight = 15.0;
graph.plotAreaFrame.paddingBottom = 70.0;
graph.plotAreaFrame.paddingLeft = 55.0;
// Grid line styles
CPTMutableLineStyle *majorGridLineStyle = [CPTMutableLineStyle lineStyle];
majorGridLineStyle.lineWidth = 0.75;
majorGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.5];
CPTMutableLineStyle *minorGridLineStyle = [CPTMutableLineStyle lineStyle];
minorGridLineStyle.lineWidth = 0.25;
minorGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.1];
// Axes
// X axis
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)graph.axisSet;
CPTXYAxis *x = axisSet.xAxis;
x.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
x.orthogonalCoordinateDecimal = CPTDecimalFromUnsignedInteger(0);
x.majorGridLineStyle = majorGridLineStyle;
x.minorGridLineStyle = minorGridLineStyle;
x.minorTicksPerInterval = 9;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = kCFDateFormatterNoStyle;
dateFormatter.timeStyle = kCFDateFormatterMediumStyle;
CPTTimeFormatter *timeFormatter = [[CPTTimeFormatter alloc] initWithDateFormatter:dateFormatter];
timeFormatter.referenceDate = referenceDate;
x.labelFormatter = timeFormatter;
// Y axis
CPTXYAxis *y = axisSet.yAxis;
y.labelingPolicy = CPTAxisLabelingPolicyAutomatic;
y.orthogonalCoordinateDecimal = CPTDecimalFromUnsignedInteger(0);
y.majorGridLineStyle = majorGridLineStyle;
y.minorGridLineStyle = minorGridLineStyle;
y.minorTicksPerInterval = 3;
y.labelOffset = 5.0;
y.axisConstraints = [CPTConstraints constraintWithLowerOffset:0.0];
// Rotate the labels by 45 degrees, just to show it can be done.
x.labelRotation = M_PI * 0.25;
// Create the plot
CPTScatterPlot *dataSourceLinePlot = [[CPTScatterPlot alloc] init] ;
dataSourceLinePlot.identifier = kPlotIdentifier;
dataSourceLinePlot.cachePrecision = CPTPlotCachePrecisionDouble;
CPTMutableLineStyle *lineStyle = [dataSourceLinePlot.dataLineStyle mutableCopy];
lineStyle.lineWidth = 3.0;
lineStyle.lineColor = [CPTColor greenColor];
dataSourceLinePlot.dataLineStyle = lineStyle;
dataSourceLinePlot.dataSource = self;
[graph addPlot:dataSourceLinePlot];
// Plot space
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(0) length:CPTDecimalFromDouble(kTimePeriod)];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(0) length:CPTDecimalFromUnsignedInteger(1)];
plotData = [[NSMutableArray alloc] init];
scrollTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / kFrameRate
target:self
selector:@selector(scrollPlot:)
userInfo:nil
repeats:YES];
newDataTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:@selector(newData:)
userInfo:nil
repeats:YES];
}
#pragma mark Test Methods
-(void)scrollPlot:(NSTimer *)theTimer
{
CPTGraph *theGraph = graphView.hostedGraph;
CPTPlot *thePlot = [theGraph plotWithIdentifier:kPlotIdentifier];
if ( thePlot ) {
currentIndex = (-[referenceDate timeIntervalSinceNow])-kTimePeriod;
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)theGraph.defaultPlotSpace;
double location = currentIndex;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromDouble(location)
length:CPTDecimalFromDouble(kTimePeriod)];
//[plotData addObject:[NSNumber numberWithDouble:(1.0 - kAlpha) * [[plotData lastObject] doubleValue] + kAlpha * rand() / (double)RAND_MAX]];
//[thePlot insertDataAtIndex:plotData.count - 1 numberOfRecords:1];
}
}
-(void)newData:(NSTimer *)theTimer
{
CPTGraph *theGraph = graphView.hostedGraph;
CPTPlot *thePlot = [theGraph plotWithIdentifier:kPlotIdentifier];
if ( thePlot ) {
DataObj *test = [[DataObj alloc] init];
test.timeInterval = -[referenceDate timeIntervalSinceNow];
test.testValue = rand() / (double)RAND_MAX;
[plotData addObject:test];
[thePlot insertDataAtIndex:plotData.count - 1 numberOfRecords:1];
}
}
#pragma mark Plot Data Source Methods
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
return [plotData count];
}
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSNumber *num = nil;
DataObj *dataObj = [plotData objectAtIndex:index];
switch ( fieldEnum ) {
case CPTScatterPlotFieldX:
num = [NSNumber numberWithDouble:dataObj.timeInterval];
break;
case CPTScatterPlotFieldY:
num = [NSNumber numberWithDouble:dataObj.testValue];
break;
default:
break;
}
return num;
}
How can i get the program to be time-accurate and less cpu consuming? Thanks.
Upvotes: 1
Views: 1044
Reputation: 27381
The automatic axis labeling policy doesn't work with dates very well. If you want to label every 20 seconds, for example, use the fixed interval labeling policy.
The high CPU usage is probably related to redrawing the axes and the plot for every plot range update. You'll need to experiment with different scrolling frame rates to find the best combination of CPU usage and smooth updates.
Upvotes: 2