fix(study): simulatePressure: true 항상 — Pencil pressure 미도달 시 속도 기반 fallback

사용자 보고: 마우스도 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) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-27 15:32:05 +09:00
parent 1a93c9cbe6
commit 56efc6ffc5
@@ -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<Stroke, 'id' | 'points' | 'size' | 'refW' | 'refH'>[] {
function serializableStrokes(): Pick<Stroke, 'id' | 'points' | 'size' | 'refW' | 'refH' | 'simPressure'>[] {
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();
}