이제 마우스드래그 이벤트는 마우스의 포인트를 Array에 업데이트만 하고,
다시 그릴 화면이 있다고 알려주기만 합니다
라인을 그리는 메서드는 NSView의 draw(_ dirtyRect: ) 메서드 안에서 호출되고,
Array에 의해 선을 그리게 될 겁니다
class Canvas: NSView {
var lineWidth = 5
var lines = [[CGPoint]]()
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
self.drawLines()
}
override func mouseDown(with event: NSEvent) {
let currentPoint = CGPoint(x: event.locationInWindow.x, y: event.locationInWindow.y)
lines.append([currentPoint])
}
override func mouseDragged(with event: NSEvent) {
guard var lastLine = lines.popLast() else { return }
let currentPoint = CGPoint(x: event.locationInWindow.x, y: event.locationInWindow.y)
lastLine.append(currentPoint)
lines.append(lastLine)
self.needsDisplay = true
}
}
선을 조금 더 부드럽게 그리기 위해서 addLine() 대신 addCurve()를 사용합니다
extension Canvas {
func drawLines(){
NSGraphicsContext.saveGraphicsState()
guard let context = NSGraphicsContext.current?.cgContext else { return }
context.setLineCap(.round)
context.setLineWidth(lineWidth)
context.setStrokeColor(NSColor.red.cgColor)
context.setBlendMode(.normal)
context.strokePath()
context.setAlpha(0.3)
context.setShouldAntialias(true)
lines.forEach { line in
for i in stride(from: 0, to: line.count, by: 3) {
if i == 0 {
context.move(to: line[i])
}else {
context.addCurve(to: line[i], control1: line[i-2], control2: line[i-1])
}
}
context.strokePath()
}
NSGraphicsContext.restoreGraphicsState()
}
}
조금은 개선된 것처럼 보입니다
하지만 아직도 그려야 할 point가 늘어날수록 CPU 사용량이 빠르게 올라갑니다
기존에는 모든 점을 이어서 하나의 경로로 연결했습니다
연결해야하는 점들이 많아질수록 path를 그리기 위한 계산 비용이 증가하기 때문에
이런 방식은 좋지 않습니다
lines.forEach { line in
for i in stride(from: 0, to: line.count, by: 3) {
if i != 0 {
context.move(to: line[i-3])
context.addCurve(to: line[i], control1: line[i-2], control2: line[i-1])
context.strokePath()
}
}
}
이제는 3개의 점이 추가될 때마다 새로운 경로를 그립니다
예를 들어, 7개의 점 ( a, b, c, d, e, f, g ) 가 있을 때
기존에는 이를 모두 이어서 하나의 선을 그렸다면
새로운 방식에서는 a, b, c, d 를 잇는 선 하나와 d, e, f, g 를 잇는 선 하나를 그립니다
점 갯수가 800개를 돌파했을 때의 CPU 사용량이 7x%에서 4x%로 내려갔네요
선이 추가될 때 화면 전체를 다시 그리지 않고 변경된 영역만 그립니다
override func mouseDragged(with event: NSEvent) {
guard var lastLine = lines.popLast() else { return }
let currentPoint = CGPoint(x: event.locationInWindow.x, y: event.locationInWindow.y)
let count = lastLine.count
lastLine.append(currentPoint)
lines.append(lastLine)
if count > 0 && count % 3 == 0 {
let rect = getRect(currentPoint, lastLine[count-3], lastLine[count-2], lastLine[count-1])
self.setNeedsDisplay(rect)
}
}
// 네개의 점이 속하는 영역을 돌려준다.
func getRect(_ point1: CGPoint, _ point2: CGPoint, _ point3: CGPoint, _ point4: CGPoint) -> CGRect {
let minX = min(point1.x, point2.x, point3.x, point4.x) - CGFloat(lineWidth / 2) // 선의 굵기만큼 영역을 더한다.
let minY = min(point1.y, point2.y, point3.y, point4.y) - CGFloat(lineWidth / 2) // 선의 굵기만큼 영역을 더한다.
let maxX = max(point1.x, point2.x, point3.x, point4.x) + CGFloat(lineWidth / 2) // 선의 굵기만큼 영역을 더한다.
let maxY = max(point1.y, point2.y, point3.y, point4.y) + CGFloat(lineWidth / 2) // 선의 굵기만큼 영역을 더한다.
let width = maxX - minX
let height = maxY - minY
return CGRect(x: minX, y: minY, width: width, height: height)
}
점 갯수가 800개를 돌파했을 때의 CPU 사용량이 4x%에서 1x%로 내려갔네요