ops(study): pressure 파이프라인 진단 패널 — raw/mapped/final 3단계 + tilt/buttons
사용자 분석: 수치 튜닝 무관해 보이면 pressure 입력 자체가 안 들어오는 케이스. perfect- freehand 옵션 변경 의미 없음. 먼저 PointerEvent.pressure 가 실제로 변동하는지 확인 필요. 진단 패널 (?debug=1) 에 추가: - PRESSURE PIPELINE 섹션: · raw = PointerEvent.pressure 원본 · mapped = getStrokePressure 의 inputP (raw 매핑 또는 속도 fallback) · final = fixedPressure update 후 perfect-freehand 에 전달되는 값 · raw min/max — 세션 내 raw pressure 범위 (사용자가 펜 강약 시도 후 확인) - tiltX, tiltY, ptr width/height, buttons — Pencil 추가 입력 필드. 판별: - raw 가 항상 0.5 또는 1.0 → 디바이스/브라우저에서 pressure 미전달. 현재 환경에서는 속도 기반 fallback 이 유일. - raw 가 변동 (0.1~1.0) 인데 mapped/final 이 일정 → 우리 코드가 무시 중. - raw + mapped + final 모두 변동 → perfect-freehand 가 무시 (thinning, simulatePressure).
This commit is contained in:
@@ -87,6 +87,19 @@
|
||||
coalesced: 0,
|
||||
lastType: '-',
|
||||
lastPressure: 0,
|
||||
// pressure 파이프라인 진단 — raw → mapped → final.
|
||||
// raw = PointerEvent.pressure 원본
|
||||
// mapped = getStrokePressure 의 inputP (raw 매핑 또는 속도 fallback)
|
||||
// final = fixedPressure update 후 perfect-freehand 에 전달되는 값
|
||||
rawP: 0,
|
||||
mappedP: 0,
|
||||
finalP: 0,
|
||||
tiltX: 0, tiltY: 0,
|
||||
ptrW: 0, ptrH: 0,
|
||||
buttons: 0,
|
||||
// raw pressure 의 세션 min/max — 사용자가 펜 강약 시도 후 확인.
|
||||
rawMin: 1,
|
||||
rawMax: 0,
|
||||
});
|
||||
// dimension 측정 — button click 시 어느 element 의 dimension 이 변하는지 진단.
|
||||
let dimDbg = $state({ canW: 0, canH: 0, conW: 0, conH: 0 });
|
||||
@@ -359,31 +372,39 @@
|
||||
// 1. inputP 결정.
|
||||
let inputP: number;
|
||||
if (Number.isFinite(rawPressure) && rawPressure > 0.1 && rawPressure < 0.99) {
|
||||
// raw pressure 0.1~0.99 → 0.6~1.0 매핑. floor 보장.
|
||||
inputP = PRESSURE_FLOOR + ((rawPressure - 0.1) / 0.89) * (1 - PRESSURE_FLOOR);
|
||||
} else {
|
||||
const last = target.points[target.points.length - 1];
|
||||
if (last) {
|
||||
const dist = Math.hypot(x - last[0], y - last[1]);
|
||||
// 속도 기반 0.6~1.0.
|
||||
inputP = Math.max(PRESSURE_FLOOR, Math.min(1.0, 1.6 - dist / 25));
|
||||
} else {
|
||||
inputP = FIRST_POINT_PRESSURE;
|
||||
}
|
||||
}
|
||||
// 2. fixed hybrid.
|
||||
let finalP: number;
|
||||
if (target.fixedPressure === undefined) {
|
||||
target.fixedPressure = inputP;
|
||||
return inputP;
|
||||
finalP = inputP;
|
||||
} else {
|
||||
const dev = Math.abs(inputP - target.fixedPressure);
|
||||
if (dev >= FIXED_LARGE) {
|
||||
target.fixedPressure = inputP;
|
||||
finalP = inputP;
|
||||
} else {
|
||||
const alpha = dev < FIXED_THRESHOLD ? FIXED_ALPHA_NOISE : FIXED_ALPHA_INTENT;
|
||||
target.fixedPressure = target.fixedPressure + (inputP - target.fixedPressure) * alpha;
|
||||
finalP = target.fixedPressure;
|
||||
}
|
||||
}
|
||||
const dev = Math.abs(inputP - target.fixedPressure);
|
||||
if (dev >= FIXED_LARGE) {
|
||||
target.fixedPressure = inputP;
|
||||
return inputP;
|
||||
// DBG: raw → mapped → final 3 단계 + raw min/max 추적.
|
||||
if (DBG) {
|
||||
const rawMin = Math.min(dbg.rawMin, rawPressure);
|
||||
const rawMax = Math.max(dbg.rawMax, rawPressure);
|
||||
dbg = { ...dbg, rawP: rawPressure, mappedP: inputP, finalP, rawMin, rawMax };
|
||||
}
|
||||
const alpha = dev < FIXED_THRESHOLD ? FIXED_ALPHA_NOISE : FIXED_ALPHA_INTENT;
|
||||
target.fixedPressure = target.fixedPressure + (inputP - target.fixedPressure) * alpha;
|
||||
return target.fixedPressure;
|
||||
return finalP;
|
||||
}
|
||||
function pushPointWithInterp(target: Stroke, x: number, y: number, p: number) {
|
||||
const last = target.points[target.points.length - 1];
|
||||
@@ -514,7 +535,19 @@
|
||||
}
|
||||
|
||||
function onPointerMove(e: PointerEvent) {
|
||||
if (DBG) dbg = { ...dbg, move: dbg.move + 1, lastType: e.pointerType, lastPressure: e.pressure };
|
||||
if (DBG) {
|
||||
dbg = {
|
||||
...dbg,
|
||||
move: dbg.move + 1,
|
||||
lastType: e.pointerType,
|
||||
lastPressure: e.pressure,
|
||||
tiltX: e.tiltX ?? 0,
|
||||
tiltY: e.tiltY ?? 0,
|
||||
ptrW: e.width ?? 0,
|
||||
ptrH: e.height ?? 0,
|
||||
buttons: e.buttons,
|
||||
};
|
||||
}
|
||||
|
||||
// 지우개 인디케이터 — hover (펜 미접촉) 만으로도 cursor 위치 추적. isDrawing
|
||||
// 가드 *전*이라 mouse hover / Pencil hover 모두 잡힘.
|
||||
@@ -934,15 +967,17 @@
|
||||
<!-- 라이브 디버그 패널 — DEV 빌드 또는 prod 에서 ?debug=1 query 시 활성. -->
|
||||
<div class="absolute top-1 left-1 px-2 py-1 rounded bg-bg/90 text-[10px] text-dim font-mono pointer-events-none leading-tight">
|
||||
tool:{tool} width:{widthMode}<br/>
|
||||
btn pen:{btnDbg.pen} er:{btnDbg.eraser} w:{btnDbg.width}<br/>
|
||||
css:{cssWidth}×{cssHeight}<br/>
|
||||
canvas:{dimDbg.canW}×{dimDbg.canH}<br/>
|
||||
container:{dimDbg.conW}×{dimDbg.conH}<br/>
|
||||
type:{dbg.lastType} p:{dbg.lastPressure.toFixed(2)}<br/>
|
||||
type:{dbg.lastType} buttons:{dbg.buttons}<br/>
|
||||
<span class="text-accent">PRESSURE PIPELINE</span><br/>
|
||||
raw:{dbg.rawP.toFixed(3)} (min:{dbg.rawMin.toFixed(2)} max:{dbg.rawMax.toFixed(2)})<br/>
|
||||
mapped:{dbg.mappedP.toFixed(3)} final:{dbg.finalP.toFixed(3)}<br/>
|
||||
tilt:{dbg.tiltX},{dbg.tiltY} ptr:{dbg.ptrW}×{dbg.ptrH}<br/>
|
||||
<span class="text-accent">EVENT COUNTERS</span><br/>
|
||||
down:{dbg.down} move:{dbg.move} up:{dbg.up} cancel:{dbg.cancel}<br/>
|
||||
rejType:{dbg.rejectedByType} rejId:{dbg.rejectedByPointerId} coal:{dbg.coalesced}<br/>
|
||||
drawing:{isDrawing ? 'Y' : 'N'} actId:{activePointerId ?? '-'} infPts:{inflight?.points.length ?? 0}<br/>
|
||||
strokes:{strokes.length}
|
||||
strokes:{strokes.length} btn pen:{btnDbg.pen} er:{btnDbg.eraser}<br/>
|
||||
css:{cssWidth}×{cssHeight} canvas:{dimDbg.canW}×{dimDbg.canH}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user