Last active
June 2, 2025 05:14
-
-
Save kangddong/5fe777c6bcb2fcca87e604245a1ff444 to your computer and use it in GitHub Desktop.
직선, 물결 그리기
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol PencilStrokeEditAble: AnyObject { | |
var startPoint: CGPoint? { get set } | |
var initialStrokeCount: Int { get set } | |
var inkTool: PKInkingTool { get set } | |
func beginStroke(at point: CGPoint, initialStrokeCount: Int) | |
func updateStroke(to point: CGPoint) -> PKStroke? | |
func endStroke() | |
func update(inkTool: PKInkingTool) | |
} | |
extension PencilStrokeEditAble { | |
func beginStroke(at point: CGPoint, initialStrokeCount: Int) { | |
self.startPoint = point | |
self.initialStrokeCount = initialStrokeCount | |
} | |
func endStroke() { | |
self.startPoint = nil | |
self.initialStrokeCount = 0 | |
} | |
func update(inkTool: PKInkingTool) { | |
self.inkTool = inkTool | |
} | |
} | |
final class StraightLineManager: PencilStrokeEditAble { | |
var startPoint: CGPoint? | |
var initialStrokeCount: Int = 0 | |
var inkTool: PKInkingTool = PKInkingTool(.pen, color: .black, width: 4) | |
func updateStroke(to point: CGPoint) -> PKStroke? { | |
guard let start = startPoint else { return nil } | |
let interpolatedWidth = inkTool.width * 0.65 | |
let size = CGSize(width: interpolatedWidth, height: interpolatedWidth) | |
let points = [ | |
PKStrokePoint(location: start, timeOffset: 0, size: size, opacity: 1, force: 1, azimuth: 0, altitude: 0), | |
PKStrokePoint(location: point, timeOffset: 1, size: size, opacity: 1, force: 1, azimuth: 0, altitude: 0) | |
] | |
let path = PKStrokePath(controlPoints: points, creationDate: Date()) | |
let ink = PKInk(inkTool.inkType, color: inkTool.color) | |
return PKStroke(ink: ink, path: path) | |
} | |
} | |
final class WavyLineManager: PencilStrokeEditAble { | |
var startPoint: CGPoint? | |
var initialStrokeCount: Int = 0 | |
var inkTool: PKInkingTool = PKInkingTool(.pen, color: .black, width: 4) | |
// 물결 효과를 위한 파라미터 | |
private let waveAmplitude: CGFloat = 5.0 // 물결의 높이 | |
private let waveFrequency: CGFloat = 3.0 // 물결의 주기 | |
private let waveLength: CGFloat = 20 // 한 파장의 길이 (px 기준) | |
func updateStroke(to point: CGPoint) -> PKStroke? { | |
guard let start = startPoint else { return nil } | |
// 시작점과 현재 점 사이 거리 및 방향 계산 | |
let dx = point.x - start.x | |
let dy = point.y - start.y | |
let distance = hypot(dx, dy) | |
// 선분 길이에 따라 세그먼트 분할 (최소 2세그먼트 이상) | |
let segments = max(Int(distance / 5), 2) | |
// 선분의 단위 방향 벡터 | |
let ux = dx / distance | |
let uy = dy / distance | |
// 법선 벡터 = (-uy, ux) | |
let perp = CGPoint(x: -uy, y: ux) | |
let interpolatedWidth = inkTool.width * 0.65 | |
let size = CGSize(width: interpolatedWidth, height: interpolatedWidth) | |
var points: [PKStrokePoint] = [] | |
for i in 0...segments { | |
let t = CGFloat(i) / CGFloat(segments) | |
// 선형 보간으로 기본 위치 계산 | |
let baseX = start.x + dx * t | |
let baseY = start.y + dy * t | |
let basePoint = CGPoint(x: baseX, y: baseY) | |
// sine 기반 offset: 2π * frequency 를 곱해 주기 조절 | |
let offset = waveAmplitude * sin((distance * t / waveLength) * 2 * .pi) | |
let offsetPoint = CGPoint( | |
x: basePoint.x + perp.x * offset, | |
y: basePoint.y + perp.y * offset | |
) | |
let strokePoint = PKStrokePoint( | |
location: offsetPoint, | |
timeOffset: Double(t), | |
size: size, | |
opacity: 1, | |
force: 1, | |
azimuth: 0, | |
altitude: 0 | |
) | |
points.append(strokePoint) | |
} | |
let path = PKStrokePath(controlPoints: points, creationDate: Date()) | |
let ink = PKInk(inkTool.inkType, color: inkTool.color) | |
return PKStroke(ink: ink, path: path) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment