最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - How to Smooth a Drawn Path in SwiftUI to Avoid Sharp Turns? - Stack Overflow

programmeradmin0浏览0评论

I am working on a drawing app in SwiftUI where users can draw freeform lines. I want to smooth the path when it's drawn so that sharp turns are not visible and the path looks smoother. Currently, I’m using a quadratic Bézier curve to draw the path, but it still has sharp corners when users change direction abruptly.

Here is my current code for drawing the path:


 @State private var dragPath: [CGPoint] = []

ZStack{

Path { path in
    guard dragPath.count >= 2 else { return }
    
    path.move(to: dragPath[0])
    
    for i in 1..<dragPath.count {
        let previousPoint = dragPath[i - 1]
        let currentPoint = dragPath[i]
        
        let controlPoint = CGPoint(
            x: (previousPoint.x + currentPoint.x) / 2,
            y: (previousPoint.y + currentPoint.y) / 2
        )
        
        path.addQuadCurve(to: currentPoint, control: controlPoint)
    }
}
.stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))


}.gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { value in
                        if !isCreated {
                            dragPath.append(value.location)
                        }
                    }
                    .onEnded { _ in isCreated = true }
            )

Is there a way to further smooth out the path to avoid any sharp turns when the user changes direction? Ideally, I want the path to appear continuous and fluid, even if the user draws in an erratic manner.

Additionally, I have attached a screenshot of the desired outcome for your reference.

this is what I want

this is the result what I got right now

I am working on a drawing app in SwiftUI where users can draw freeform lines. I want to smooth the path when it's drawn so that sharp turns are not visible and the path looks smoother. Currently, I’m using a quadratic Bézier curve to draw the path, but it still has sharp corners when users change direction abruptly.

Here is my current code for drawing the path:


 @State private var dragPath: [CGPoint] = []

ZStack{

Path { path in
    guard dragPath.count >= 2 else { return }
    
    path.move(to: dragPath[0])
    
    for i in 1..<dragPath.count {
        let previousPoint = dragPath[i - 1]
        let currentPoint = dragPath[i]
        
        let controlPoint = CGPoint(
            x: (previousPoint.x + currentPoint.x) / 2,
            y: (previousPoint.y + currentPoint.y) / 2
        )
        
        path.addQuadCurve(to: currentPoint, control: controlPoint)
    }
}
.stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))


}.gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { value in
                        if !isCreated {
                            dragPath.append(value.location)
                        }
                    }
                    .onEnded { _ in isCreated = true }
            )

Is there a way to further smooth out the path to avoid any sharp turns when the user changes direction? Ideally, I want the path to appear continuous and fluid, even if the user draws in an erratic manner.

Additionally, I have attached a screenshot of the desired outcome for your reference.

this is what I want

this is the result what I got right now

Share Improve this question edited Feb 5 at 4:02 Hadi Fooladi Talari 1,2781 gold badge15 silver badges39 bronze badges asked Feb 4 at 10:27 HeWhoRemainsHeWhoRemains 6111 bronze badges 3
  • How are the points for the path being collected? Do you add a point for every drag movement, which means there are many (perhaps thousands) of points, mostly very close to each other? Or do you add a single point for every drag release, as it is being done in this answer? For the first approach, you could try using a rolling average to smooth the path. For the second approach, there might be ways of computing better control points for the arcs. – Benzy Neez Commented Feb 4 at 11:54
  • let me add whole Code that how I added points – HeWhoRemains Commented Feb 4 at 12:19
  • Quick searching turns up many, many resources for "smoothing" a path. The first one I looked at: exploringswift/blog/… appears pretty thorough. – DonMag Commented Feb 4 at 12:28
Add a comment  | 

1 Answer 1

Reset to default 1

The way you are collecting the drag points is to add a new point for every drag movement. This will give a path with many (perhaps thousands) of points, mostly very close to each other.

I would suggest using a rolling average to smooth these points.

In your original screenshot, you actually have two paths. The smaller path (the point of the arrow) probably has only a few points. For smaller paths, you probably don't want to apply so much smoothing.

Here is an updated version of your example to show the path being smoothed in this way:

struct ContentView: View {
    private typealias Points = [CGPoint]
    @State private var allPaths = [Points]()
    @State private var dragPath = Points()
    let maxOffsetForAverage = 5

    private func drawPath(points: Points) -> some View {
        Path { path in
            let nPoints = points.count
            let maxOffset = max(1, min(maxOffsetForAverage, nPoints / maxOffsetForAverage))
            var xSum = CGFloat.zero
            var ySum = CGFloat.zero
            var previousRangeBegin = 0
            var previousRangeEnd = 0
            for i in 0..<nPoints {
                let rangeBegin = max(0, i - maxOffset)
                let rangeEnd = min(nPoints - 1, i + maxOffset)
                if i == 0, let firstPoint = points.first {
                    path.move(to: firstPoint)
                    for point in points[rangeBegin...rangeEnd] {
                        xSum += point.x
                        ySum += point.y
                    }
                } else {
                    if rangeBegin > previousRangeBegin {
                        let previousPoint = points[previousRangeBegin]
                        xSum -= previousPoint.x
                        ySum -= previousPoint.y
                    }
                    if rangeEnd > previousRangeEnd {
                        let endPoint = points[rangeEnd]
                        xSum += endPoint.x
                        ySum += endPoint.y
                    }
                    let sampleSize = CGFloat(rangeEnd - rangeBegin + 1)
                    let point = CGPoint(x: xSum / sampleSize, y: ySum / sampleSize)
                    path.addLine(to: point)
                }
                previousRangeBegin = rangeBegin
                previousRangeEnd = rangeEnd
            }
            if nPoints > 2, let lastPoint = points.last {
                path.addLine(to: lastPoint)
            }
        }
        .stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round))
    }

    var body: some View {
        VStack {
            ZStack {
                Image(systemName: "ladybug")
                    .resizable()
                    .scaledToFit()
                ForEach(Array(allPaths.enumerated()), id: \.offset) { offset, points in
                    drawPath(points: points)
                }
                if !dragPath.isEmpty {
                    drawPath(points: dragPath)
                }
            }
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { val in
                        dragPath.append(val.location)
                    }
                    .onEnded { val in
                        var currentPath = dragPath
                        currentPath.append(val.location)
                        allPaths.append(currentPath)
                        dragPath.removeAll()
                    }
            )
            .frame(width: 300, height: 300)
            .border(.gray)

            Button("Reset") {
                allPaths.removeAll()
            }
            .buttonStyle(.bordered)
            .padding()
        }
    }
}

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>