@ -297,6 +297,16 @@ function dotn(a, b) {
@@ -297,6 +297,16 @@ function dotn(a, b) {
return r ;
}
function dotn3 ( a , b , c ) {
let r = 0 ;
for ( let i = 0 ; i < a . length ; ++ i ) {
r += a [ i ] * b [ i ] * c [ i ] ;
}
return r ;
}
function mix ( a , b , t ) {
return a * t + b * ( 1 - t ) ;
}
@ -478,9 +488,75 @@ function stroke_intersects_capsule(state, stroke, a, b, radius) {
@@ -478,9 +488,75 @@ function stroke_intersects_capsule(state, stroke, a, b, radius) {
return false ;
}
function estimate _next _point ( ts , xs , ys , dt = 0 ) {
function proj _remove _x _from _y ( xs , ys , ws ) {
const dxx = dotn3 ( xs , xs , ws ) ;
const dxy = dotn3 ( xs , ys , ws ) ;
if ( dxx < 1e-6 ) {
return [ 0 , ys ] ;
}
const c = dxy / dxx ;
const res = [ ] ;
for ( let i = 0 ; i < xs . length ; ++ i ) {
res . push ( ys [ i ] - c * xs [ i ] ) ;
}
return [ c , res ] ;
}
function estimate _next _point2 ( ts , xs , ys , wr = 1.0 ) {
const N = ts . length ;
if ( N < 2 ) {
return [ xs [ N - 1 ] , ys [ N - 1 ] ] ;
}
// inner product weight, power-law ramp over time from W0 to 1.0 (max)
const t0 = ts [ 0 ] ;
const t1 = ts [ N - 1 ] ;
const ws = [ ] ;
// constant and quadratic basis terms
const q0 = [ ] ;
const ss = [ ] ;
for ( const t of ts ) {
ws . push ( 1 + ( ( wr - 1 ) * ( t - t0 ) / ( t1 - t0 ) ) ) ;
q0 . push ( 1 ) ;
ss . push ( t * t ) ;
}
// constant term
const [ c0x , xs _0 ] = proj _remove _x _from _y ( q0 , xs , ws ) ;
const [ c0y , ys _0 ] = proj _remove _x _from _y ( q0 , ys , ws ) ;
const [ c0t , ts _0 ] = proj _remove _x _from _y ( q0 , ts , ws ) ;
const [ c0s , ss _0 ] = proj _remove _x _from _y ( q0 , ss , ws ) ;
// linear term
const [ c01x , xs _01 ] = proj _remove _x _from _y ( ts _0 , xs _0 , ws ) ;
const [ c01y , ys _01 ] = proj _remove _x _from _y ( ts _0 , ys _0 , ws ) ;
// don't need to do ts here because it's guaranteed to go to zero
const [ c01s , ss _01 ] = proj _remove _x _from _y ( ts _0 , ss _0 , ws ) ;
// quadratic term
const [ c012x , xs _012 ] = proj _remove _x _from _y ( ss _01 , xs _0 , ws ) ;
const [ c012y , ys _012 ] = proj _remove _x _from _y ( ss _01 , ys _0 , ws ) ;
const reconstructed _x = c0x * 1 + c01x * ts _0 [ N - 1 ] + c012x * ss _01 [ N - 1 ] ;
const reconstructed _y = c0y * 1 + c01y * ts _0 [ N - 1 ] + c012y * ss _01 [ N - 1 ] ;
return [ reconstructed _x , reconstructed _y ] ;
}
function estimate _next _point1 ( ts , xs , ys , dt = 0 ) {
const N = ts . length ;
if ( N < 2 ) {
return [ xs [ N - 1 ] , ys [ N - 1 ] ] ;
}
// mean values
let mx = 0 , my = 0 , mt = 0 ;
@ -494,10 +570,6 @@ function estimate_next_point(ts, xs, ys, dt=0) {
@@ -494,10 +570,6 @@ function estimate_next_point(ts, xs, ys, dt=0) {
mx /= N ;
my /= N ;
if ( N < 2 ) {
return [ xs [ N - 1 ] , ys [ N - 1 ] ] ;
}
// orthogonalize against constant term
for ( let i = 0 ; i < N ; ++ i ) {
ts [ i ] -= mt ;
@ -548,7 +620,7 @@ function smooth_curve(points, window_ms=config.demetri_ms, dt=0) {
@@ -548,7 +620,7 @@ function smooth_curve(points, window_ms=config.demetri_ms, dt=0) {
y _window . push ( p . y ) ;
}
const [ nx , ny ] = estimate _next _point ( t _window , x _window , y _window , dt ) ;
const [ nx , ny ] = estimate _next _point2 ( t _window , x _window , y _window , config . wr ) ;
result . push ( {
'x' : nx ,