Successfully reported this slideshow.
Your SlideShare is downloading. ×

Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 74 Ad

Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기

Download to read offline

PencilKit? 올가미 툴?
모두 모르신다면 대환영입니다
PencilKit에 대한 소개부터 올가미 툴에 대한 내용까지 알차게 준비했습니다
올가미 기능을 구현하기 위해 어떤 고민을 했는지 함께 파헤쳐보러 가볼까요

PencilKit? 올가미 툴?
모두 모르신다면 대환영입니다
PencilKit에 대한 소개부터 올가미 툴에 대한 내용까지 알차게 준비했습니다
올가미 기능을 구현하기 위해 어떤 고민을 했는지 함께 파헤쳐보러 가볼까요

Advertisement
Advertisement

More Related Content

Recently uploaded (20)

Advertisement

Let'Swift 2022 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기

  1. 1. 이해석 PencilKit과 Point in Polygon 알고리즘을 활용한 올가미 툴 개발기 콴다(매스프레소)
  2. 2. 목차 1.올가미 툴이란? 2.PencilKit 알아보기 3.어떻게 구현 했나요? 4.마무리
  3. 3. 올가미 툴이란?
  4. 4. GoodNotes Notability
  5. 5. QANDA
  6. 6. PencilKit 알아보기
  7. 7. PencilKit Framework iOS 13+ iPadOS 13+ macOS 10.15+ (macOS Catalina) iOS 14+ iPadOS 14+ macOS 11.0+ (macOS Big Sur)
  8. 8. PencilKit PKDrawing PKStroke PKInk PKTool PKToolPicker PKCanvasView PKStrokePath PKStrokePoint PKInkingTool PKLassoTool PKEraserTool
  9. 9. PencilKit PKDrawing PKStroke PKInk PKTool PKToolPicker PKCanvasView PKStrokePath PKStrokePoint PKInkingTool PKLassoTool PKEraserTool
  10. 10. PKCanvasView iOS 13+
  11. 11. PKDrawing iOS 13+
  12. 12. QANDA
  13. 13. PKInk PKStrokePath PKStrokePoint PKStroke iOS 14+
  14. 14. Location / Force Azimuth Altitude
  15. 15. PKToolPicker iOS 13+
  16. 16. PKTool iOS 13+ PKEraserTool PKLassoTool PKInkingTool Ruler
  17. 17. PKLassoTool 👎 PKStroke 이외에도 사용자가 PKCanvasView 위에 올린 이미지 / 텍스트 (UIView)에도 올가미의 기능을 적용해야 함 UI/ 기능 확장의 어려움
  18. 18. 올가미 기능 PKStroke 뿐만 아니라 PKCanvasView 위에 올린 이미지 또는 텍스트 뷰를 잡을 수 있어야 함 올가미가 아이템들을 잡은 상태에서 아이템들의 위치를 이동시킬 수 있어야함
  19. 19. 어떻게 구현했나요?
  20. 20. 올가미 안에 어떤 아이템이 포함 되었는지 포함되지 않았는지 어떻게 알 수 있을까?
  21. 21. 다각형 안에 어떤 점들이 포함 되었는지 포 함되지 않았는지 어떻게 알 수 있을까?
  22. 22. Point in Polygon Problem https://en.wikipedia.org/wiki/Point_in_polygon 평면에서 주어진 점이 다각형의 내부 혹은 외부에 있는지를 판별하는 문제
  23. 23. Ray casting algorithm https://en.wikipedia.org/wiki/Point_in_polygon O(N) 시간 복잡도 crossing number 알고리즘 혹은 even-odd rule 알고리즘이라고 함
  24. 24. https://en.wikipedia.org/wiki/Point_in_polygon
  25. 25. 1. 반직선과 변의 교점의 개수가 짝수면 외부에 있는 점, 홀수면 내부에 있는 점 2. 변 위에 있는 점은 내부에 있는 점 3. 반직선이 꼭짓점을 지날때 위쪽과 아래쪽 변 중 한 개만 교차한다고 판단함 4. 반직선이 변과 평행한 경우는 고려하지 않음
  26. 26. 1. 반직선과 변의 교점의 개수가 짝수면 외부에 있는 점, 홀수면 내부에 있는 점 2. 변 위에 있는 점은 내부에 있는 점 3. 반직선이 꼭짓점을 지날때 위쪽과 아래쪽 변 중 한 개만 교차한다고 판단함 4. 반직선이 변과 평행한 경우는 고려하지 않음
  27. 27. 1. 반직선과 변의 교점의 개수가 짝수면 외부에 있는 점, 홀수면 내부에 있는 점 2. 변 위에 있는 점은 내부에 있는 점 3. 반직선이 꼭짓점을 지날때 위쪽과 아래쪽 변 중 한 개만 교차한다고 판단함 4. 반직선이 변과 평행한 경우는 고려하지 않음
  28. 28. 1. 반직선과 변의 교점의 개수가 짝수면 외부에 있는 점, 홀수면 내부에 있는 점 2. 변 위에 있는 점은 내부에 있는 점 3. 반직선이 꼭짓점을 지날때 위쪽과 아래쪽 변 중 한 개만 교차한다고 판단함 4. 반직선이 변과 평행한 경우는 고려하지 않음
  29. 29. 1. 반직선과 변의 교점의 개수가 짝수면 외부에 있는 점, 홀수면 내부에 있는 점 2. 변 위에 있는 점은 내부에 있는 점 3. 반직선이 꼭짓점을 지날때 위쪽과 아래쪽 변 중 한 개만 교차한다고 판단함 4. 반직선이 변과 평행한 경우는 고려하지 않음
  30. 30. PKStroke
  31. 31. PKStroke 을 점으로 표현할 수 없을까?
  32. 32. WWDC20: inspect, modify, and construct PencilKit drawings
  33. 33. UIView 는 어떻게 잡을 수 있을까?
  34. 34. UIImageView CGRect의 contains?
  35. 35. UIImageView
  36. 36. 더 정밀하게 잡을 수 없을까?
  37. 37. UIImageView를 점들로 표현해보자!
  38. 38. UIImageView
  39. 39. UIImageView
  40. 40. 기준점을 원점으로 보냄 원점을 기준으로 Transform 원위치로 다시 이동 1 2 3
  41. 41. (xr, yr) (xr, yr) 1 2 3
  42. 42. import UIKit extension UIView { var frameBeforeTransformed: CGRect { let previousTransform = transform transform = .identity let frame = frame transform = previousTransform return frame } func transformedEdges(cases: [CGRectEdgeType] = CGRectEdgeType.allCases) -> [CGPoint] { let result = cases.map { edgeType -> CGPoint in let point = frameBeforeTransformed.point(type: edgeType) let movedPoint = pointBeforeTransformed(point) let transformedPoint = movedPoint.applying(transform) return pointAfterTransformed(transformedPoint) } return result } func pointBeforeTransformed(_ point: CGPoint) -> CGPoint { return .init(x: point.x - center.x, y: point.y - center.y); } func pointAfterTransformed(_ point: CGPoint) -> CGPoint { return .init(x: point.x + center.x, y: point.y + center.y); } } import UIKit enum CGRectEdgeType: CaseIterable { case leftTop case leftBottom case rightTop case rightBottom } extension CGRect { func point(type: CGRectEdgeType) -> CGPoint { switch type { case .leftTop: return CGPoint(x: minX, y: minY) case .leftBottom: return CGPoint(x: minX, y: maxY) case .rightTop: return CGPoint(x: maxX, y: minY) case .rightBottom: return CGPoint(x: maxX, y: maxY) } } } 1 2 3
  43. 43. UIImageView
  44. 44. Accelerate 고성능 & 낮은 에너지 소비에 최적화된 수학/이미지 계산 라이브러리
  45. 45. Accelerate: vDSP_vgenp(_:_:_:_:_:_:_:_)
  46. 46. func linearInterpolation(point1: CGPoint, point2: CGPoint) -> [CGPoint] { let (start, end) = point2.x < point1.x ? (point2, point1) : (point1, point2) let (startX, endX) = (Float(start.x), Float(end.x)) let (startY, endY) = (Float(start.y), Float(end.y)) let (deltaX, deltaY) = (abs(endX - startX), abs(endY - startY)) let maxDelta = max(deltaX, deltaY) let (indices, stride, n) = ([0, maxDelta], vDSP_Stride(1), vDSP_Length(maxDelta)) var xPoints = [Float](repeating: 0, count: Int(n)) var yPoints = [Float](repeating: 0, count: Int(n)) var values = [startX, endX] vDSP_vgenp( values, stride, indices, stride, &xPoints, stride, n, vDSP_Length(values.count) ) values = [startY, endY] vDSP_vgenp( values, stride, indices, stride, &yPoints, stride, n, vDSP_Length(values.count) ) return zip(xPoints, yPoints).map { CGPoint(x: CGFloat($0), y: CGFloat($1)) } }
  47. 47. UIImageView
  48. 48. Edge case를 찾아보자
  49. 49. Case1
  50. 50. Case2
  51. 51. Case3
  52. 52. Case3
  53. 53. Case3
  54. 54. 올가미로 잡은 아이템을 어떻게 옮길 수 있을까?
  55. 55. UIView를 옮기는 건 쉬운데… PKStroke은 어떻게 옮길 수 있을까?
  56. 56. UIPanGestureRecognizer Logic Began 올가미로 잡은 PKStroke을 PKDrawing에서 분리 Changed Ended 올가미로 잡은 PKStroke에 transform 적용 transform 된 PKStroke 생성
  57. 57. import PencilKit extension PKStroke { func createNewStroke(with transform: CGAffineTransform) -> PKStroke { let dx = transform.tx let dy = transform.ty let resultPoint: [PKStrokePoint] = path.map { p in return PKStrokePoint( location: CGPoint(x: (p.location.x + dx), y: (p.location.y + dy)), timeOffset: p.timeOffset, size: p.size, opacity: p.opacity, force: p.force, azimuth: p.azimuth, altitude: p.altitude ) } let resultPath = PKStrokePath(controlPoints: resultPoint, creationDate: Date()) var resultStroke = PKStroke(ink: PKInk(ink.inkType, color: ink.color), path: resultPath, transform: .identity) if let mask = mask { mask.apply(transform) resultStroke.mask = mask } return resultStroke } }
  58. 58. 올가미로 잡은 PKStroke에 transform 적용 transform 된 PKStroke 생성 UIPanGestureRecognizer Logic Began 올가미로 잡은 PKStroke을 PKDrawing에서 분리 Changed Ended 새로운 PKStroke을 PKCanvasView의 PKDrawing에 반영
  59. 59. QANDA
  60. 60. UIPanGestureRecognizer Logic Began 올가미로 잡은 PKStroke에 transform 적용 transform 된 PKStroke 생성 올가미로 잡은 PKStroke을 PKDrawing에서 분리 ⚠ 랜더링 병목 발생! 새로운 PKStroke을 PKCanvasView의 PKDrawing에 반영 Changed Ended
  61. 61. UIPanGestureRecognizer Logic Began 올가미로 잡은 PKStroke을 PKDrawing에서 분리 분리한 PKStroke로 새로운 PKDrawing을 생성 새로운 PKDrawing을 이미지로 변환 / 이미지 뷰 생성 (Snapshot)
  62. 62. PKDrawing: image(from: CGRect, scale: CGFloat)
  63. 63. UIPanGestureRecognizer Logic Began 올가미로 잡은 PKStroke을 PKDrawing에서 분리 Changed 분리한 PKStroke로 새로운 PKDrawing을 생성 새로운 PKDrawing을 이미지로 변환 / 이미지 뷰 생성 (Snapshot) 이미지 뷰를 transform 시킴 transform 된 PKStroke 생성 이미지 뷰 제거 새로운 PKStroke을 PKCansvasView의 PKDrawing에 반영 Ended
  64. 64. dragging ? UIPanGestureRecognizer Began Changed Ended true false reset lasso points append lasso point draw bezier path append lasso point draw bezier path append lasso point draw bezier path captured? false reset lasso points detach captured strokes Ray casting algorithm move uiviews move image view (strokes) attach captured strokes detach image view Ray casting algorithm attach image view
  65. 65. attach image view false detach captured strokes move uiviews move image view (strokes) attach captured strokes detach image view dragging ? UIPanGestureRecognizer Began Changed Ended true reset lasso points append lasso point draw bezier path append lasso point draw bezier path captured? false reset lasso points Ray casting algorithm Ray casting algorithm append lasso point draw bezier path
  66. 66. append lasso point draw bezier path Ray casting algorithm true reset lasso points append lasso point draw bezier path append lasso point draw bezier path captured? false reset lasso points false detach captured strokes move uiviews move image view (strokes) attach captured strokes detach image view dragging ? UIPanGestureRecognizer Began Changed Ended Ray casting algorithm attach image view
  67. 67. QANDA
  68. 68. Apple Documentation PencilKit https://developer.apple.com/documentation/pencilkit Accelerate https://developer.apple.com/documentation/accelerate
  69. 69. WWDC Introducing PencilKit https://developer.apple.com/videos/play/wwdc2019/221/ What’s new in PencilKit https://developer.apple.com/videos/play/wwdc2020/10107/ Inspect,modify,and construct PencilKit drawings https://developer.apple.com/videos/play/wwdc2020/10148
  70. 70. 콴다 기술 블로그 iOS PencilKit을 활용한 Drawing 노트 개발기 https://blog.mathpresso.com/ios-pencilkit을-활용한-drawing-노트-개발기- d50019d42041
  71. 71. 이해석 감사합니다 콴다(매스프레소) Special Thanks 신연화 님 / QANDA iOS 팀 / 이정엽, 정초이

×