fix(study): visual continuity — pressure floor 0.5 + thinning 0.35

사용자 보고: 빠른 stroke 가 점선처럼 끊겨 보임 ("선이 이어지지 않음").

원인: 속도/raw pressure 기반 inputP floor 가 0.25 ~ 0.3 → thinning 0.5 적용 시
outline 폭이 size × 0.5 미만 → 픽셀 단위 정렬 안 되면 dot 패턴.

Fix:
- 속도 기반 inputP floor 0.25 → 0.5. 가장 빠른 stroke 도 size × 0.825 폭 보장.
- raw pressure 매핑 0.3~1.0 → 0.5~1.0. min 폭 보장.
- thinning 0.5 → 0.35. 변동 폭 줄임 (min 폭 더 보장).

Trade-off: 굵기 변동 폭 줄어듦. 하지만 사용자 우선순위 = visual continuity.
inputP 0.5~1.0 + thinning 0.35 → 폭 변동 ±17.5% (충분히 보임).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-27 15:47:02 +09:00
parent fb73f96d2e
commit 1ba425f07a
@@ -218,8 +218,10 @@
if (!path) {
const outline = getStroke(pts, {
size,
// thinning 0.5 — 큰 압력 변화 시 굵기 차이 명확. fixedPressure 가 잡음 흡수.
thinning: 0.5,
// thinning 0.35 — 변동 폭 줄여 min stroke 폭 보장. visual continuity (점선
// 끊김 차단) 우선. 큰 압력 변화 시도 stroke 폭 차이는 inputP floor 0.5
// 와 max 1.0 사이 = 50% 변동 → 폭 ±17.5% 변화 (충분히 보임).
thinning: 0.35,
smoothing: 0.99,
streamline: 0.75,
// simulatePressure: false — getStrokePressure 가 raw pressure / 속도 추정 /
@@ -358,19 +360,20 @@
const FIXED_ALPHA_NOISE = 0.03;
const FIXED_ALPHA_INTENT = 0.5;
function getStrokePressure(target: Stroke, x: number, y: number, rawPressure: number): number {
// 1. inputP 결정.
// 1. inputP 결정. floor 0.5 — 가장 약한 입력에도 stroke 가 픽셀 단위 끊김
// 없이 visual continuity 유지 (사용자 보고: 빠른 stroke 가 점선처럼 끊김).
let inputP: number;
if (Number.isFinite(rawPressure) && rawPressure > 0.1 && rawPressure < 0.99) {
// raw pressure 활용 — 0.1~0.99 → 0.3~1.0 으로 dynamic range 확장.
inputP = 0.3 + ((rawPressure - 0.1) / 0.89) * 0.7;
// raw pressure 0.1~0.99 → 0.5~1.0 매핑. floor 0.5.
inputP = 0.5 + ((rawPressure - 0.1) / 0.89) * 0.5;
} else {
// 속도 기반. 변동 폭 0.25~1.0 (이전 0.3~1.0 보다 넓음).
const last = target.points[target.points.length - 1];
if (last) {
const dist = Math.hypot(x - last[0], y - last[1]);
inputP = Math.max(0.25, Math.min(1.0, 1.6 - dist / 18));
// 속도 기반 0.5~1.0. 빠른 stroke 도 가늘어지지만 minimum 폭 보장.
inputP = Math.max(0.5, Math.min(1.0, 1.6 - dist / 25));
} else {
inputP = 0.55;
inputP = 0.7;
}
}
// 2. fixed hybrid.