【问题标题】:Unaccounted horizontal lines on real time plot using Core-Plot使用 Core-Plot 在实时绘图上未计算的水平线
【发布时间】:2014-09-08 21:56:24
【问题描述】:

我正在尝试在同一张图上使用 4 个单独的图绘制 4 通道输入流的实时图。目前我正在从文本文件中提取这些数据,但最终会使用蓝牙来获取这些数据。我的绘图本身工作正常,但 core-plot 似乎在绘图开始时在第一个数据集的水平上为每个绘图绘制了一条额外的水平线。我认为这与 core-plot 试图在开始显示它们之前绘制所有点有关。下图显示了这些线条,而实际波形(方波)正在绘制的中间。

随着绘图的进行和 x 轴的显示范围发生变化,这些线会消失。喜欢这里:

以下是我的代码的相关部分。它基于 Ray Wenderlich 的散点图教程和 Eric Skrooch 的实时绘图示例。 (我希望我把这些名字拼写对了!)。就个人而言,我认为这是核心情节中的错误,而不是编码错误。非常感谢任何有关如何摆脱这些令人不安的线条的帮助!谢谢!

-(void)configurePlots {

    //Get graph and plot space
    CPTGraph *graph = self.hostView.hostedGraph;
    CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) graph.defaultPlotSpace;

    //Create the four plots
    CPTScatterPlot *channel1Plot = [[CPTScatterPlot alloc]init];
    channel1Plot.dataSource = self;
    channel1Plot.identifier = @"channel1";
    [graph addPlot:channel1Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel2Plot = [[CPTScatterPlot alloc]init];
    channel2Plot.dataSource = self;
    channel2Plot.identifier = @"channel2";
    [graph addPlot:channel2Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel3Plot = [[CPTScatterPlot alloc]init];
    channel3Plot.dataSource = self;
    channel3Plot.identifier = @"channel3";
    [graph addPlot:channel3Plot toPlotSpace:plotSpace];

    CPTScatterPlot *channel4Plot = [[CPTScatterPlot alloc]init];
    channel4Plot.dataSource = self;
    channel4Plot.identifier = @"channel4";
    [graph addPlot:channel4Plot toPlotSpace:plotSpace];

    //Set up plot space
    [plotSpace scaleToFitPlots:[NSArray arrayWithObjects:channel1Plot, channel2Plot, channel3Plot, channel4Plot, nil]];
    CPTMutablePlotRange *xRange = [plotSpace.xRange mutableCopy];
    [xRange expandRangeByFactor:CPTDecimalFromCGFloat(1.1f)];
    plotSpace.xRange = xRange;
    CPTMutablePlotRange *yRange = [plotSpace.yRange mutableCopy];
    [yRange expandRangeByFactor:CPTDecimalFromCGFloat(1.2f)];
    plotSpace.yRange = yRange;

    //Create styles and symbols

    CPTMutableLineStyle *channelLineStyle = [channel1Plot.dataLineStyle mutableCopy];
    channelLineStyle.lineWidth = 1.0;
    channelLineStyle.lineColor = [CPTColor redColor];
    channel1Plot.dataLineStyle = channelLineStyle;
    channel2Plot.dataLineStyle = channelLineStyle;
    channel3Plot.dataLineStyle = channelLineStyle;
    channel4Plot.dataLineStyle = channelLineStyle;

}

-(void)configureAxes {

    //Create styles
    CPTMutableTextStyle *axisTitleStyle = [CPTMutableTextStyle textStyle];
    axisTitleStyle.color = [CPTColor whiteColor];
    axisTitleStyle.fontName = @"Helvetica-Bold";
    axisTitleStyle.fontSize = 12.0f;
    CPTMutableLineStyle *axisLineStyle = [CPTMutableLineStyle lineStyle];
    axisLineStyle.lineWidth = 2.0f;
    axisLineStyle.lineColor = [CPTColor whiteColor];
    CPTMutableTextStyle *axisTextStyle = [[CPTMutableTextStyle alloc] init];
    axisTextStyle.color = [CPTColor yellowColor];
    axisTextStyle.fontName = @"Helvetica-Bold";
    axisTextStyle.fontSize = 11.0f;
    CPTMutableLineStyle *tickLineStyle = [CPTMutableLineStyle lineStyle];
    tickLineStyle.lineWidth = 2.0f;
    tickLineStyle.lineColor = [CPTColor yellowColor];
    CPTMutableLineStyle *gridLineStyle = [CPTMutableLineStyle lineStyle];
    tickLineStyle.lineColor = [CPTColor purpleColor];
    tickLineStyle.lineWidth = 1.0f;

    //Get axis set
    CPTXYAxisSet *axisSet = (CPTXYAxisSet *) self.hostView.hostedGraph.axisSet;

    //Configure X-axis
    CPTAxis *x = axisSet.xAxis;
    x.title = @"Time";
    x.titleTextStyle = axisTitleStyle;
    x.titleOffset = 15.0f;
    x.axisLineStyle = axisLineStyle;
    x.labelingPolicy = CPTAxisLabelingPolicyNone;
    x.labelTextStyle = axisTextStyle;
    x.majorTickLineStyle = axisLineStyle;
    x.majorTickLength = 4.0f;
    x.tickDirection = CPTSignNegative;
    CGFloat pointsCount = POINTS_ON_SCREEN;
    NSMutableSet *xLabels = [NSMutableSet setWithCapacity:pointsCount];
    NSMutableSet *xLocations = [NSMutableSet setWithCapacity:pointsCount];
    NSInteger i=0;
    //NSString *loc = [NSString stringWithFormat:@"d",j];
    for (NSInteger j=0; j<POINTS_ON_SCREEN; j++) {
        CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:@"%ld", (long)j+1] textStyle:x.labelTextStyle];
        CGFloat location = i++;
        label.tickLocation = CPTDecimalFromCGFloat(location);
        label.offset = x.majorTickLength;
        if (label) {
            [xLabels addObject:label];
            [xLocations addObject:[NSNumber numberWithFloat:location]];
        }
    }
    x.axisLabels = xLabels;
    x.majorTickLocations = xLocations;

    //Configure Y-axis
    axisSet.yAxis.axisConstraints = [CPTConstraints constraintWithLowerOffset:0.0];
    CPTAxis *y = axisSet.yAxis;
    y.title = @"Channel outputs";
    y.titleTextStyle = axisTitleStyle;
    y.titleOffset = -40.0f;
    y.axisLineStyle = axisLineStyle;
    y.majorGridLineStyle = gridLineStyle;
    y.labelingPolicy = CPTAxisLabelingPolicyNone;
    y.labelTextStyle = axisTextStyle;
    y.labelOffset = 16.0f;
    y.majorTickLineStyle = axisLineStyle;
    y.majorTickLength = 4.0f;
    y.minorTickLength = 2.0f;
    y.tickDirection = CPTSignPositive;
    CGFloat majorIncrement = 0.0005;
    CGFloat minorIncrement = 0.0001;
    CGFloat yMax = 0.0040f;
    NSMutableSet *yLabels = [NSMutableSet set];
    NSMutableSet *yMajorLocations = [NSMutableSet set];
    NSMutableSet *yMinorLocations = [NSMutableSet set];
    for (CGFloat j = minorIncrement; j<=yMax; j+=minorIncrement) {
        j = roundf(j*100000)/100000;
        CGFloat mod = j / majorIncrement;
        NSInteger modInt = (NSInteger)mod;
        CGFloat modMantissa = mod - modInt;
        modMantissa = roundf(modMantissa * 100)/100;
        if (modMantissa < 0.1 || modMantissa > 0.9) {
            CPTAxisLabel *label = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:@"%.5f", j] textStyle:y.labelTextStyle];
            NSDecimal location = CPTDecimalFromCGFloat(j);
            //NSNumber *location = [NSNumber numberWithFloat:j];
            label.tickLocation = location;
            label.offset = -y.majorTickLength -y.labelOffset -9;
            //label.offset = 0;
            if (label) {
                [yLabels addObject:label];
            }
            [yMajorLocations addObject:[NSDecimalNumber decimalNumberWithDecimal:location]];
        } else {
            [yMinorLocations addObject:[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f",j]]];
        }
    }
    y.axisLabels = yLabels;
    y.majorTickLocations = yMajorLocations;
    y.minorTickLocations = yMinorLocations;
}

-(void)createTimer {
    NSTimer *dataTimer = [NSTimer timerWithTimeInterval:0.002 target:self selector:@selector(dynamicUpdate:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:dataTimer forMode:NSDefaultRunLoopMode];
}

-(void)dynamicUpdate: (NSTimer *)dataTimer {
    CPTGraph *graph = self.hostView.hostedGraph;
    CPTPlot *channel1Plot = [graph plotWithIdentifier:@"channel1"];
    CPTPlot *channel2Plot = [graph plotWithIdentifier:@"channel2"];
    CPTPlot *channel3Plot = [graph plotWithIdentifier:@"channel3"];
    CPTPlot *channel4Plot = [graph plotWithIdentifier:@"channel4"];
    CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;


    if (channel1Plot && channel2Plot && channel3Plot && channel4Plot) {
        if ( arrayIndex1 >= POINTS_ON_SCREEN-1
            || arrayIndex2 >= POINTS_ON_SCREEN-1
            || arrayIndex3 >= POINTS_ON_SCREEN-1
            || arrayIndex4 >= POINTS_ON_SCREEN-1) {

            [channel1Array removeObjectAtIndex:0];
            [channel1Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel2Array removeObjectAtIndex:0];
            [channel2Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel3Array removeObjectAtIndex:0];
            [channel3Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
            [channel4Array removeObjectAtIndex:0];
            [channel4Plot deleteDataInIndexRange:NSMakeRange(0, 1)];
        }

        plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(currentIndex >= POINTS_ON_SCREEN ? currentIndex-POINTS_ON_SCREEN +1 : 0) length:CPTDecimalFromUnsignedInteger(POINTS_ON_SCREEN-1)];
        currentIndex++;

        [channel1Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel2Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel3Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
        [channel4Plot insertDataAtIndex:POINTS_ON_SCREEN-1 numberOfRecords:1];
    }
}


-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot {
    return POINTS_ON_SCREEN;
}

-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)idx {
   // NSLog(@"Plotting point");
    NSNumber *num = nil;
    if ([self.activityIndicator isAnimating] == YES) {
        [self.activityIndicator stopAnimating];
    }
    switch (fieldEnum) {

        case CPTScatterPlotFieldX:
            if (idx < POINTS_ON_SCREEN) {
                return [NSNumber numberWithUnsignedInteger:idx + currentIndex - POINTS_ON_SCREEN];
            };
            break;

        case CPTScatterPlotFieldY:
            if ([plot.identifier isEqual:@"channel1"]) {
                num = [channel1Array objectAtIndex:idx];
                arrayIndex1 = idx;
            } else if ([plot.identifier isEqual:@"channel2"]) {
                num = [channel2Array objectAtIndex:idx];
                arrayIndex2 = idx;
            } else if ([plot.identifier isEqual:@"channel3"]) {
                num = [channel3Array objectAtIndex:idx];
                arrayIndex3 = idx;
            } else if ([plot.identifier isEqual:@"channel4"]) {
                num = [channel4Array objectAtIndex:idx];
                arrayIndex4 = idx;
            } else {
                NSLog(@"data unavailable");
            }
            break;

        default:
            NSLog(@"ERROR: trying to plot on unidentified axis");
            break;
    }
    NSString *numString = [NSString stringWithFormat:@"%@", num];
    dispatch_async(AddToArrayQ, ^{[self writeData:numString];});
    return num;
}

【问题讨论】:

标签: ios objective-c core-plot


【解决方案1】:

除非我读错了,否则您将从 numberOfRecordsForPlot: 返回一个硬编码值,因此 CorePlot 期望能够填充 x 轴上的所有这些点。您看到的那些行可能是由于已将这些数组初始化为一个常量值(它们必须被初始化,对,否则当您尝试访问未初始化的索引时会出错?)

请注意,在 Eric Skroch 的示例中,他正在从 numberOfRecordsForPlot: 返回 [plotData count],并且 plotData 在初始化时为空。

【讨论】:

    【解决方案2】:

    这里的问题不在于像 RishiG 建议的那样硬编码 numberOfRecordsForPlot: 方法,而在于在 numberForPlot:field:recordIndex 方法中硬编码案例 CPTScatterPlotFieldX。不过 RishiG 的建议还是不错的,我在解决问题之前做了一些修改。

    原文:数组 channel1Array - channel4Array 已经包含要绘制的数据,我在绘制时继续从数组中删除项目。这意味着我不能在 numberOfRecordsForPlot: 方法中使用类似 [channel1Array count] 的东西,而不得不依赖绘图索引。这也可行,但无论如何效率低下。 修改:完全按照 Eric Skroch 的示例从空数组开始,并随着绘图的进行更新它们。

    此处使用的 numberForPlot:field:recordIndex 方法中的原始案例 CPTScatterPlotFieldX 的返回值为 (idx + currrentIndex - POINTS_ON_SCREEN),这可能会产生否定结果。看起来 core-plot 可能使用它的绝对值来获得 x 坐标,从而获得水平线。这可以通过删除 if 条件并将返回值为 (idx + currentIndex - channel1Array.count) 来解决。

    P.S.:@vikingosegundo 我知道,谢谢。但是我必须快速连续拍照,所以我的相机的连拍模式最适合!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多