【问题标题】:UIBezierPath - smoothing chart of choppy/noisy dataUIBezierPath - 波涛汹涌/嘈杂数据的平滑图表
【发布时间】:2017-11-26 17:12:07
【问题描述】:

我的目标是在我的用户在屏幕上滑动时实现类似 Adob​​e Illustrator 的线条。目前情况非常不稳定-这是当前情况的屏幕截图:

我认为我想要什么很明显。我希望那些直线被平滑。这叫做抗锯齿吗?

- (void)touchesBegan: (CGPoint)point
{
    currentStroke = [[Stroke alloc] initWithStartPoint:point];
    [strokes addObject:currentStroke];
}

- (void)touchesMoved: (CGPoint)point
{
    if (!currentStroke) {
        [self touchesBegan:point];
    }
    [currentStroke.points addObject:[NSValue valueWithCGPoint:point]]
    pathTwo = [self createPath];
    [self scaleToFit];
    [self setNeedsDisplay];
}

- (UIBezierPath *)createPath {
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    for (Stroke * stroke in strokes) {
        [bezierPath moveToPoint:stroke.startPoint];
        for (NSValue * value in stroke.points) {
            [bezierPath addLineToPoint:[value CGPointValue]];
        }
    }
    return bezierPath;
}

- (void)touchesEnded: (CGPoint)point
{
    [points removeAllObjects];
    [self setNeedsDisplay];
}

drawRect 里面有这个:

[[UIColor whiteColor] setStroke];
[pathTwo setLineWidth: _strokeWidth];
[pathTwo stroke];

编辑

我想我找到了答案…… https://github.com/jnfisher/ios-curve-interpolation

鉴于我的createPath 公式,我只是不知道如何使用它。

也许有人可以帮我插入我的数组?

【问题讨论】:

  • 不,这不是抗锯齿问题。您需要访问构成整条线的各个点并对它们应用平滑操作,这将类似于 1)获取起点和终点,然后将其替换为中间的直线或 2)平均多个段的斜率等。有很多方法可以平滑曲线。
  • 你能给我看一下选项#2的代码吗?
  • 您的数据是如何存储的?您是否有权访问点 (x,y) 值数组?我可以提供帮助,但重要的是您开始使用什么。换句话说,每个点是否也有“时间”或一些指示,使其相对于线以正确的顺序排列?你打算如何重绘它?你需要先确定它会起作用。
  • 我将数据存储在 NSMutableArray 中,首先是“stroke”(touchBegan) 以及它与 touchEnd 之间的所有关联点击...。
  • 我在你的帖子中看到了 Bezier 这个词,这实际上是一个很好的算法来平滑事物。您也许可以做一些事情,例如每 5 或 10 个点取一个,并将它们作为贝塞尔曲线的控制点,而忽略其余的点。你想谷歌的是:“曲线拟合线点到平滑线”,或“贝塞尔曲线拟合点”。计算机科学/数学的艺术术语是“曲线拟合”

标签: ios objective-c uibezierpath


【解决方案1】:

问题在于,您在一个手势中捕获的一系列点之间划出一条路径,而这些点并不都在同一条线上,因为它们包括用户手指在屏幕上拖动时发生的自然偏差.

这与“抗锯齿”完全无关,“抗锯齿”是一种使直线看起来更平滑的技术,不会分散视觉上的“锯齿”。但是这里的问题不是你试图改善单条直线的渲染,而是你一开始没有画直线,而是画了一堆线段正好排队。

我的目标是当我的用户在屏幕上滑动时实现类似 Adob​​e Illustrator 的线条

在这种情况下,您不需要“抗锯齿”。您只需要一个createPath

  • moveToPoint到第一点;和
  • addLineToPoint 到最后一点。

只是不要担心中间的任何点。这个简单的解决方案将生成一条直线,该直线将从您触摸开始的地方开始,一直延伸到您的触摸结束的地方,并且会一直持续到您抬起手指为止。

我认为这将转化为:

- (UIBezierPath *)createPath {
    UIBezierPath * bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:stroke.startPoint];
    [bezierPath addLineToPoint:[[stroke.points lastObject] CGPointValue]];
    return bezierPath;
}

如果您不想要一条实际的线,而只是想平滑这条线,我建议 (a) 进行滚动平均以消除有弹性数据点中的峰和谷; (b) 使用 Hermite 或 Catmull-Rom 样条(如 https://spin.atomicobject.com/2014/05/28/ios-interpolating-points/ 中所述),因此生成的路径是平滑的,而不是一系列线段。

因此

- (UIBezierPath * _Nullable)createActualPathFor:(Stroke *)stroke {
    if (stroke.points.count < 2) return nil;

    UIBezierPath * path = [UIBezierPath bezierPath];

    [path moveToPoint:[stroke.points.firstObject CGPointValue]];
    for (NSInteger i = 1; i < stroke.points.count; i++) {
        [path addLineToPoint:[stroke.points[i] CGPointValue]];
    }

    return path;
}

- (UIBezierPath * _Nullable)createSmoothedPathFor:(Stroke *)stroke interval:(NSInteger)interval {
    if (stroke.points.count < 2) return nil;

    NSMutableArray <NSValue *> *averagedPoints = [NSMutableArray arrayWithObject:stroke.points[0]];

    NSInteger current = 1;
    NSInteger count = 0;
    do {
        CGFloat sumX = 0;
        CGFloat sumY = 0;
        do {
            CGPoint point = [stroke.points[current] CGPointValue];
            sumX += point.x;
            sumY += point.y;
            current++;
            count++;
        } while (count < interval && current < stroke.points.count);

        if (count >= interval) {
            CGPoint average = CGPointMake(sumX / count, sumY / count);
            [averagedPoints addObject:[NSValue valueWithCGPoint:average]];
            count = 0;
        }
    } while (current < stroke.points.count);

    if (count != 0) {
        [averagedPoints addObject:stroke.points.lastObject];
    }

    return [UIBezierPath interpolateCGPointsWithHermite:averagedPoints closed:false];
}

将产生以下结果(实际数据点为蓝色,平滑后的再现为红色):

请注意,我假设您的 pointsStroke 数组包含起点,因此您可能需要相应地进行调整。

【讨论】:

【解决方案2】:

感谢大家的支持。我离开了一段时间。但我找到了一个非常好的解决我的情况的方法。这是一个进行插值的类。

https://github.com/jnfisher/ios-curve-interpolation

我所要做的就是传递我的点数数组,然后将它们平滑掉!

【讨论】:

    猜你喜欢
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 2015-11-07
    • 2014-08-02
    相关资源
    最近更新 更多