From fb73f96d2ec24ec7d398a8711cfa9116fa4c3ce8 Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 27 Apr 2026 15:44:05 +0900 Subject: [PATCH] =?UTF-8?q?fix(study):=20=EA=B0=95=ED=95=9C=20=EC=95=95?= =?UTF-8?q?=EB=A0=A5=20=EC=A6=89=EC=8B=9C=20=EB=B0=98=EC=9D=91=20=E2=80=94?= =?UTF-8?q?=203=EB=8B=A8=EA=B3=84=20threshold=20+=20dynamic=20range=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=20+=20thinning=20=ED=82=A4=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 보고: 빡세게 눌러도 굵기 차이 거의 안 남. 원인 분석: 1. raw pressure 0.1~0.99 만 활용했는데 dynamic range 그대로 → 변동 작음. 2. 속도 기반 변동 폭 0.3~1.0 작음 + dist/25 비율 작음. 3. INTENT alpha 0.25 너무 느림 → 강한 변화도 stroke 내내 못 따라감. 4. thinning 0.4 변동 폭 부족. Fix: - raw pressure 0.1~0.99 → 0.3~1.0 으로 매핑. dynamic range 확장. - 속도 기반 0.25~1.0 + 비율 dist/18. 변동 폭 키움. - 3단계 threshold: · dev < 0.15 (잡음) → alpha 0.03 (fixed 유지) · 0.15 ≤ dev < 0.3 (의도적) → alpha 0.5 (이전 0.25 → 빠르게 따라감) · dev ≥ 0.3 (매우 큼, 빡세게 누름) → 즉시 update (alpha 1.0) - thinning 0.4 → 0.5. 폭 변동 더 명확. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/lib/components/HandwriteCanvas.svelte | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/frontend/src/lib/components/HandwriteCanvas.svelte b/frontend/src/lib/components/HandwriteCanvas.svelte index b4321d7..151daa7 100644 --- a/frontend/src/lib/components/HandwriteCanvas.svelte +++ b/frontend/src/lib/components/HandwriteCanvas.svelte @@ -218,9 +218,8 @@ if (!path) { const outline = getStroke(pts, { size, - // thinning 0.4 — fixedPressure 가 평소 잡음을 흡수하므로 변동 폭 키워도 안정. - // 의도적 큰 압력 변화 시만 굵기 변화 명확히 보임. - thinning: 0.4, + // thinning 0.5 — 큰 압력 변화 시 굵기 차이 명확. fixedPressure 가 잡음 흡수. + thinning: 0.5, smoothing: 0.99, streamline: 0.75, // simulatePressure: false — getStrokePressure 가 raw pressure / 속도 추정 / @@ -350,35 +349,41 @@ // 부드러운 곡선 생성 → 보간 점 적게 둘 수록 일정 간격 vertex 패턴 (= 사용자 // 보고 "선 사이사이 마디") 안 발생. 단 30px+ gap 은 보간으로 점선 방지. const MAX_GAP_PX = 16; - // Smart pressure — fixed 유지 (잡음/평소) + 의도적 큰 변화 시 따라감. - // 사용자 시나리오: - // - Pencil 정상 + 평소 압력 → fixed 유지 (Notability felt: stroke 내부 균일) - // - Pencil 정상 + 의도적 압력 변화 → 따라감 (굵기 변화) - // - Pencil 비정상 / mouse → raw pressure 가 0 또는 일정 → 속도 기반 추정. - const FIXED_THRESHOLD = 0.15; // 변동 < 15% 잡음, ≥ 15% 의도적 + // Smart pressure — 3단계 threshold. + // 잡음 (dev < 15%) → alpha 0.03 (거의 무시, fixed 유지) + // 의도적 (15% ≤ dev < 30%) → alpha 0.5 (빠르게 따라감) + // 매우 큼 (dev ≥ 30%) → 즉시 update (사용자 빡세게 누름 같은 큰 변화) + const FIXED_THRESHOLD = 0.15; + const FIXED_LARGE = 0.3; const FIXED_ALPHA_NOISE = 0.03; - const FIXED_ALPHA_INTENT = 0.25; + const FIXED_ALPHA_INTENT = 0.5; function getStrokePressure(target: Stroke, x: number, y: number, rawPressure: number): number { - // 1. inputP 결정 — raw pressure 정상이면 그것, 비정상이면 속도 기반. + // 1. inputP 결정. let inputP: number; - if (Number.isFinite(rawPressure) && rawPressure > 0.05 && rawPressure < 0.99) { - inputP = rawPressure; + 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; } 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]); - // 빠른 (큰 dist) = 약함, 느린 (작은 dist) = 강함. - inputP = Math.max(0.3, Math.min(1.0, 1.5 - dist / 25)); + inputP = Math.max(0.25, Math.min(1.0, 1.6 - dist / 18)); } else { inputP = 0.55; } } - // 2. fixed 초기화 (첫 점) 또는 hybrid update. + // 2. fixed hybrid. if (target.fixedPressure === undefined) { target.fixedPressure = inputP; return inputP; } const dev = Math.abs(inputP - target.fixedPressure); + if (dev >= FIXED_LARGE) { + // 매우 큰 변화 — 즉시 update. + target.fixedPressure = inputP; + return inputP; + } const alpha = dev < FIXED_THRESHOLD ? FIXED_ALPHA_NOISE : FIXED_ALPHA_INTENT; target.fixedPressure = target.fixedPressure + (inputP - target.fixedPressure) * alpha; return target.fixedPressure;