fix: Initial smoothing improved
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
@@ -266,7 +266,7 @@ const generateElementCanvas = (
|
||||
|
||||
const rc = rough.canvas(canvas);
|
||||
|
||||
drawElementOnCanvas(element, rc, context, renderConfig);
|
||||
drawElementOnCanvas(element, rc, context, renderConfig, scale);
|
||||
|
||||
context.restore();
|
||||
|
||||
@@ -402,6 +402,7 @@ const drawElementOnCanvas = (
|
||||
rc: RoughCanvas,
|
||||
context: CanvasRenderingContext2D,
|
||||
renderConfig: StaticCanvasRenderConfig,
|
||||
scale = 1,
|
||||
) => {
|
||||
switch (element.type) {
|
||||
case "rectangle":
|
||||
@@ -429,7 +430,7 @@ const drawElementOnCanvas = (
|
||||
}
|
||||
case "freedraw": {
|
||||
context.save();
|
||||
drawFreeDrawSegments(element, context, renderConfig, 0);
|
||||
drawFreeDrawSegments(element, context, renderConfig, 0, undefined, scale);
|
||||
context.restore();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -161,11 +161,13 @@ const drawSubdividedSegment = (
|
||||
t0y: number,
|
||||
t1x: number,
|
||||
t1y: number,
|
||||
scale: number,
|
||||
) => {
|
||||
const segLen = Math.sqrt((p1x - p0x) ** 2 + (p1y - p0y) ** 2);
|
||||
// Target spacing is in screen pixels; divide by scale to get scene units.
|
||||
const nSubdiv = Math.max(
|
||||
1,
|
||||
Math.ceil(segLen / BEZIER_SUBDIVIDE_TARGET_SPACING),
|
||||
Math.ceil((segLen * scale) / BEZIER_SUBDIVIDE_TARGET_SPACING),
|
||||
);
|
||||
|
||||
// Cubic Bezier control points derived from Catmull-Rom tangents.
|
||||
@@ -220,6 +222,7 @@ export const drawFreeDrawSegments = (
|
||||
renderConfig: StaticCanvasRenderConfig,
|
||||
fromIndex: number,
|
||||
upToIndex?: number,
|
||||
scale = 1,
|
||||
) => {
|
||||
const { points, pressures } = element;
|
||||
const N = points.length;
|
||||
@@ -296,6 +299,7 @@ export const drawFreeDrawSegments = (
|
||||
t0[1],
|
||||
t1[0],
|
||||
t1[1],
|
||||
scale,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -546,6 +550,7 @@ export const generateOrUpdateFreeDrawIncrementalCanvas = (
|
||||
renderConfig,
|
||||
committedFromIndex,
|
||||
newCommittedCount, // upToIndex - stop before the last provisional segment
|
||||
canvasScale,
|
||||
);
|
||||
});
|
||||
inc.committedPointCount = newCommittedCount;
|
||||
@@ -567,6 +572,7 @@ export const generateOrUpdateFreeDrawIncrementalCanvas = (
|
||||
renderConfig,
|
||||
inc.committedPointCount - 1,
|
||||
undefined, // draw to natural end (the tip segment)
|
||||
canvasScale,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -9495,6 +9495,15 @@ class App extends React.Component<AppProps, AppState> {
|
||||
let prevRawY: number | null = null;
|
||||
let prevTs: number | null = null; // previous event timestamp (ms)
|
||||
|
||||
// Pen pressure warmup: the first few samples from a pen digitizer are
|
||||
// unreliable (often spike to ~0.5). We apply an EMA whose alpha ramps
|
||||
// from PEN_PRESSURE_INITIAL_ALPHA up to 1 over PEN_PRESSURE_WARMUP_SAMPLES
|
||||
// so the stroke gradually eases from heavy smoothing into raw pressure.
|
||||
let penPressureEma: number | null = null;
|
||||
let penPressureSampleCount = 0;
|
||||
const PEN_PRESSURE_WARMUP_SAMPLES = 20;
|
||||
const PEN_PRESSURE_INITIAL_ALPHA = 0.1;
|
||||
|
||||
// Tuning constants:
|
||||
// MIN_CUTOFF – cutoff frequency (Hz) when speed ≈ 0; smaller = more smoothing
|
||||
// BETA – rate at which cutoff grows with speed; larger = quicker adaptation
|
||||
@@ -10236,7 +10245,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||
emaDy = alphaD * rawDyDt + (1 - alphaD) * emaDy;
|
||||
|
||||
// 2. Adaptive cutoff: higher speed → higher cutoff → less lag.
|
||||
const speed = Math.sqrt(emaDx * emaDx + emaDy * emaDy);
|
||||
// Normalize to screen-space pixels/sec so BETA behaves the same
|
||||
// regardless of zoom level (scene units are 1/zoom px at high zoom).
|
||||
const speed =
|
||||
Math.sqrt(emaDx * emaDx + emaDy * emaDy) *
|
||||
this.state.zoom.value;
|
||||
const cutoff = MIN_CUTOFF + BETA * speed;
|
||||
const alphaP = emaAlpha(cutoff, dt);
|
||||
|
||||
@@ -10257,7 +10270,27 @@ class App extends React.Component<AppProps, AppState> {
|
||||
if (!lastPoint || lastPoint[0] !== dx || lastPoint[1] !== dy) {
|
||||
const pt = pointFrom<LocalPoint>(dx, dy);
|
||||
newPoints.push(pt);
|
||||
newPressures.push(ev.pressure);
|
||||
|
||||
let pressure = ev.pressure;
|
||||
if (event.pointerType === "pen") {
|
||||
// Gradually ease from aggressive smoothing into raw pressure to
|
||||
// suppress the unreliable spike at the start of a pen stroke.
|
||||
const progress = Math.min(
|
||||
1,
|
||||
penPressureSampleCount / PEN_PRESSURE_WARMUP_SAMPLES,
|
||||
);
|
||||
const alpha =
|
||||
PEN_PRESSURE_INITIAL_ALPHA +
|
||||
progress * (1 - PEN_PRESSURE_INITIAL_ALPHA);
|
||||
penPressureEma =
|
||||
penPressureEma === null
|
||||
? pressure
|
||||
: alpha * pressure + (1 - alpha) * penPressureEma;
|
||||
pressure = penPressureEma;
|
||||
penPressureSampleCount++;
|
||||
}
|
||||
|
||||
newPressures.push(pressure);
|
||||
lastPoint = pt;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user