@needle-tools/facefilter
Version:
Needle Engine FaceFilter
80 lines • 3.41 kB
JavaScript
import { Matrix4 } from 'three';
export class OneEuroFilterMatrix4 {
minCutoff;
beta;
dcutoff;
lastTime = null;
xFilter;
dxFilter;
/**
* @param minCutoff - Minimum cutoff frequency. This affects the smoothness of the filter.
* Lower values result in smoother but less responsive filtering.
* Higher values make the filter more responsive but potentially more jittery.
* Typical range is 0.0001 to 1. Default is 1.
* @param beta - Speed coefficient. This affects how the filter adapts to quick changes.
* Higher values result in faster response to quick movements, but may introduce more jitter.
* Lower values have a steadier response, but may feel sluggish for quick movements.
* Typical range is 0 to 1. Default is 0.
* @param dcutoff - Cutoff frequency for derivative. This is usually set to 1, but you can adjust it
* to change how the filter responds to changes in speed.
* Lower values will make the filter more robust to sudden changes, but less responsive.
* Higher values will make it more responsive, but potentially more prone to jitter.
* Typical range is 0.1 to 1. Default is 1.
**/
constructor(minCutoff = 1, beta = 0, dcutoff = 1) {
this.minCutoff = minCutoff;
this.beta = beta;
this.dcutoff = dcutoff;
this.xFilter = new LowPassFilter(new Matrix4());
this.dxFilter = new LowPassFilter(new Matrix4());
}
filter(matrix, timestamp) {
if (this.lastTime === null) {
this.lastTime = timestamp;
this.xFilter.setAlpha(0);
this.dxFilter.setAlpha(0);
return matrix.clone();
}
const dt = Math.max(timestamp - this.lastTime, 1e-5);
this.lastTime = timestamp;
const dValue = this.subtractMatrices(matrix, this.xFilter.lastRawValue);
const dCorrected = this.dxFilter.filterWithAlpha(dValue, this.alpha(this.dcutoff, dt));
const cutoff = this.minCutoff + this.beta * dCorrected.elements.reduce((sum, val) => sum + Math.abs(val), 0);
return this.xFilter.filterWithAlpha(matrix, this.alpha(cutoff, dt));
}
alpha(cutoff, dt) {
const te = 1.0 / (2 * Math.PI * cutoff);
return 1 / (1 + te / dt);
}
subtractMatrices(a, b) {
const result = new Matrix4();
for (let i = 0; i < 16; i++) {
result.elements[i] = a.elements[i] - b.elements[i];
}
return result;
}
}
class LowPassFilter {
lastRawValue;
lastValue;
constructor(initialValue) {
this.lastRawValue = initialValue.clone();
this.lastValue = initialValue.clone();
}
setAlpha(alpha) {
if (alpha < 0 || alpha > 1) {
throw new Error("alpha should be in (0, 1]");
}
this.lastValue.copy(this.lastRawValue);
}
filterWithAlpha(value, alpha) {
this.lastRawValue.copy(value);
const result = new Matrix4();
for (let i = 0; i < 16; i++) {
result.elements[i] = alpha * value.elements[i] + (1 - alpha) * this.lastValue.elements[i];
}
this.lastValue.copy(result);
return result;
}
}
//# sourceMappingURL=utils.filter.js.map