ios 画线平滑_ios-iPhone平滑草图绘制算法

ios-iPhone平滑草图绘制算法

我正在开发iPhone上的素描应用程序。我让它正常工作,但是看起来不像这里

XqONh.png

我正在寻找任何建议来平滑绘图基本上,我所做的是当用户将手指放在我叫过的屏幕上时

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

然后我用一个阵列收集一次触摸

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

当用户从屏幕上离开手指时,我打电话给

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

然后我使用绘制数组中的所有点

NSMutableArray *points = [collectedArray points];

CGPoint firstPoint;

[[points objectAtIndex:0] getValue:&firstPoint];

CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);

CGContextSetLineCap(context, kCGLineCapRound);

CGContextSetLineJoin(context, kCGLineJoinRound);

for (int i=1; i < [points count]; i++) {

NSValue *value = [points objectAtIndex:i];

CGPoint point;

[value getValue:&point];

CGContextAddLineToPoint(context, point.x, point.y);

}

CGContextStrokePath(context);

UIGraphicsPushContext(context);

现在,我想改善绘图效果,使其更像“素描本”应用程序

zIfjJ.png

我认为信号处理算法可以重新排列阵列中的所有点,但我不确定。 任何帮助将非常感激。

提前致谢 :)

6个解决方案

56 votes

CGPoint midPoint(CGPoint p1, CGPoint p2)

{

return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);

}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

UITouch *touch = [touches anyObject];

previousPoint1 = [touch previousLocationInView:self];

previousPoint2 = [touch previousLocationInView:self];

currentPoint = [touch locationInView:self];

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

{

UITouch *touch = [touches anyObject];

previousPoint2 = previousPoint1;

previousPoint1 = [touch previousLocationInView:self];

currentPoint = [touch locationInView:self];

// calculate mid point

CGPoint mid1 = midPoint(previousPoint1, previousPoint2);

CGPoint mid2 = midPoint(currentPoint, previousPoint1);

UIGraphicsBeginImageContext(self.imageView.frame.size);

CGContextRef context = UIGraphicsGetCurrentContext();

[self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)];

CGContextMoveToPoint(context, mid1.x, mid1.y);

// Use QuadCurve is the key

CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);

CGContextSetLineCap(context, kCGLineCapRound);

CGContextSetLineWidth(context, 2.0);

CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);

CGContextStrokePath(context);

self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

}

kyoji answered 2019-10-05T18:34:05Z

22 votes

像这样平滑曲线的最简单方法是使用贝塞尔曲线而不是直线线段。 有关其背后的数学信息,请参见本文(在此答案中指向),该文章描述了如何计算平滑通过多个点的曲线所需的曲线。

我相信Core Plot框架现在可以平滑绘图的曲线,因此您可以查看那里用于实现这种平滑处理的代码。

这没有什么神奇的,因为这些平滑例程是快速且相对容易实现的。

Brad Larson answered 2019-10-05T18:33:48Z

15 votes

我真的很喜欢这个话题。 感谢所有实现,尤其是KrzysztofZabłocki和Yu-Sen Han。我已经修改了Yu-Sen Han的版本,以便根据平移速度(实际上是最后一次触摸之间的距离)来更改线条的粗细。 我还实现了点图绘制(对于touchBegan和touchEnded位置彼此靠近)结果如下:

xurNV.png

为了定义线宽,我选择了距离的函数:

(不要问我为什么……虽然很合适,但我敢肯定您会找到更好的一个)

ryumH.jpg

CGFloat dist = distance(previousPoint1, currentPoint);

CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2;

还有一个提示。 为确保厚度平滑变化,我根据上一段的厚度和自定义系数限制了边界:

self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF);

alexburtnik answered 2019-10-05T18:34:49Z

5 votes

我将kyoji的答案翻译为Swift,作为TouchDrawImageView.swift的可重用子类。子类TouchDrawImageView允许用户用手指在图像视图上进行绘制。

在将此TouchDrawImageView.swift类添加到您的项目后,请确保打开情节提要和

选择TouchDrawImageView.swift作为图像视图的“自定义类”

检查图像视图的“已启用用户交互”属性

这是TouchDrawImageView.swift的代码:

import UIKit

class TouchDrawImageView: UIImageView {

var previousPoint1 = CGPoint()

override func touchesBegan(_ touches: Set, with event: UIEvent?) {

guard let touch = touches.first else { return }

previousPoint1 = touch.previousLocation(in: self)

}

override func touchesMoved(_ touches: Set, with event: UIEvent?) {

guard let touch = touches.first else { return }

let previousPoint2 = previousPoint1

previousPoint1 = touch.previousLocation(in: self)

let currentPoint = touch.location(in: self)

// calculate mid point

let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2)

let mid2 = midPoint(p1: currentPoint, p2: previousPoint1)

UIGraphicsBeginImageContext(self.frame.size)

guard let context = UIGraphicsGetCurrentContext() else { return }

if let image = self.image {

image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))

}

context.move(to: mid1)

context.addQuadCurve(to: mid2, control: previousPoint1)

context.setLineCap(.round)

context.setLineWidth(2.0)

context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0)

context.strokePath()

self.image = UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsEndImageContext()

}

func midPoint(p1: CGPoint, p2: CGPoint) -> CGPoint {

return CGPoint(x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0)

}

}

Lars Blumberg answered 2019-10-05T18:35:39Z

3 votes

感谢您的输入。我在这里更新任务,因为我需要空间。

我查找了您建议的corePlot和Bezier曲线解决方案,但收效甚微。

对于corePlot,我可以从一个int数组中获取图形图,但是找不到与曲线平滑相关的任何东西。BTW在这里,我使用带有一些随机数的CPScatterPlot。

0kKOT.png

至于Bezier曲线,我的追求将我引到了这里这与iOS中的样条线实现有关

CatmullRomSpline *myC = [[CatmullRomSpline alloc] initAtPoint:CGPointMake(1.0, 1.0)];

[myC addPoint:CGPointMake(1.0, 1.5)];

[myC addPoint:CGPointMake(1.0, 1.15)];

[myC addPoint:CGPointMake(1.0, 1.25)];

[myC addPoint:CGPointMake(1.0, 1.23)];

[myC addPoint:CGPointMake(1.0, 1.24)];

[myC addPoint:CGPointMake(1.0, 1.26)];

NSLog(@"xxppxx %@",[myC asPointArray]);

NSLog(@"xxppxx2 %@",myC.curves);

我得到的结果是:

2011-02-24 14:45:53.915 DVA[10041:40b] xxppxx (

"NSPoint: {1, 1}",

"NSPoint: {1, 1.26}"

)

2011-02-24 14:45:53.942 DVA[10041:40b] xxppxx2 (

"QuadraticBezierCurve: 0x59eea70"

)

我不太确定该怎么走。 所以我也被困在那前面:(

我确实将GLPaint查找为最后一个资源。 它使用OpenGLES,并使用“软点”子图在数组中绘制点。 我知道这更像是避免问题而不是解决问题。 但是我想我还是会在这里分享我的发现。

黑色是GLPaint,白色是旧方法。 最后一个是“ Sketch Book”应用中的绘图

8BgdG.png

asSwE.png

WBuK0.png

我仍在努力做到这一点,非常欢迎任何进一步的建议。

Suwitcha Sugthana answered 2019-10-05T18:36:57Z

1 votes

摆脱GLPaint代码中的傻点。

在某一方面的变化

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

这个功能

//Ändrat av OLLE

/*

// Convert touch point from UIView referential to OpenGL one (upside-down flip)

if (firstTouch) {

firstTouch = NO;

previousLocation = [touch previousLocationInView:self];

previousLocation.y = bounds.size.height - previousLocation.y;

} else {

location = [touch locationInView:self];

location.y = bounds.size.height - location.y;

previousLocation = [touch previousLocationInView:self];

previousLocation.y = bounds.size.height - previousLocation.y;

}

*/

location = [touch locationInView:self];

location.y = bounds.size.height - location.y;

previousLocation = [touch previousLocationInView:self];

previousLocation.y = bounds.size.height - previousLocation.y;

//Ändrat av OLLE//

我知道这不是解决我们问题的方法,但是可以解决。

Olle answered 2019-10-05T18:37:40Z

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值