From 56efc6ffc511bee7ae21d1db84c60fd650902f9b Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 27 Apr 2026 15:32:05 +0900 Subject: [PATCH] =?UTF-8?q?fix(study):=20simulatePressure:=20true=20?= =?UTF-8?q?=ED=95=AD=EC=83=81=20=E2=80=94=20Pencil=20pressure=20=EB=AF=B8?= =?UTF-8?q?=EB=8F=84=EB=8B=AC=20=EC=8B=9C=20=EC=86=8D=EB=8F=84=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 보고: 마우스도 Pencil 도 굵기 변화 없음. iPadOS Safari 의 일부 빌드에서 Apple Pencil PointerEvent.pressure 가 정상 도달 안 하거나 일정 → 우리 thinning 0.55 적용해도 input pressure 가 일정이라 효과 0. Fix: perfect-freehand 의 simulatePressure: true 항상. - 점 간 속도 (거리) 기반 자동 pressure 추정. - 빠른 stroke = 가늘게, 천천히 = 굵게. - Notability 도 동일 felt (속도 기반 ink flow). - pen 의 실제 pressure 는 무시되지만, 들어오지 않는 빌드에서는 어차피 무관. stroke 별 simPressure 필드 / serializableStrokes 로직은 유지 (향후 분기 옵션 위해). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/lib/components/HandwriteCanvas.svelte | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/HandwriteCanvas.svelte b/frontend/src/lib/components/HandwriteCanvas.svelte index 3d376e5..d8db44c 100644 --- a/frontend/src/lib/components/HandwriteCanvas.svelte +++ b/frontend/src/lib/components/HandwriteCanvas.svelte @@ -32,6 +32,10 @@ size?: number; refW?: number; refH?: number; + // mouse stroke 는 pressure 정보 없음 (항상 0.5 fallback) → 굵기 변화 0. + // 그 경우 perfect-freehand 의 속도 기반 simulatePressure 사용 = 빠른 stroke + // 가늘게, 느린 stroke 굵게. Pencil 은 실제 pressure 사용. + simPressure?: boolean; _path2d?: Path2D; }; export type StrokesJson = { version: 1; strokes: Stroke[] }; @@ -118,13 +122,14 @@ // Path2D 등 런타임 캐시 (`_` prefix) 제외하고 직렬화. refW/refH 는 그렸을 시점의 // cssWidth/cssHeight — 다른 환경 (창 크기 다른 데스크톱, 모바일 등) 에서 load 시 // 비례 보정에 사용. 없으면 load 시점의 cssWidth/cssHeight 가 기준이 됨. - function serializableStrokes(): Pick[] { + function serializableStrokes(): Pick[] { return strokes.map((s) => ({ id: s.id, points: s.points, size: s.size, refW: s.refW, refH: s.refH, + simPressure: s.simPressure, })); } // backup 은 stroke 완료마다 호출되지만 실제 sync I/O (JSON.stringify + localStorage @@ -214,13 +219,14 @@ if (!path) { const outline = getStroke(pts, { size, - // thinning 0.55 — 압력 변동에 stroke 폭 큰 변동. 종이 만년필 reference 의 - // 5:1 굵기 차이를 부분적으로 재현 (MIN_PRESSURE 0.25 floor 와 조합 시 실제 - // 비율 약 2.4:1). Notability felt 의 핵심 = 압력에 따른 큰 굵기 차이. thinning: 0.55, smoothing: 0.99, streamline: 0.75, - simulatePressure: false, + // simulatePressure: true 항상. Apple Pencil 도 일부 iPadOS 빌드에서 실제 + // pressure 가 PointerEvent 에 정상 도달 안 하거나 일정 → 굵기 변화 0. + // 속도 기반 시뮬 (점 간 거리로 자동 추정) 이 더 robust + Notability 도 속도 + // 기반 felt. 빠른 stroke = 가늘게, 천천히 = 굵게. + simulatePressure: true, last: !isInflight, // cap: false — stroke 끝의 round cap 이 짧은 stroke 에선 dot 처럼 보이는 // 회귀. taper 가 stroke 끝을 자연스럽게 마무리하므로 cap 불필요. @@ -481,6 +487,9 @@ size: effectiveSize, refW: cssWidth, refH: cssHeight, + // pen 외 (mouse / touch) 는 속도 기반 시뮬. pressure 정보 없거나 무시되는 경우 + // 굵기 변화 보장. + simPressure: e.pointerType !== 'pen', }; scheduleRedraw(); }