1 module des.math.method.approx.interp; 2 3 import std.algorithm; 4 import std.exception; 5 import std.math; 6 import std.traits; 7 8 version(unittest) import des.math.linear.vector; 9 10 import des.ts; 11 import des.math.combin; 12 import des.stdx.traits : hasBasicMathOp; 13 14 /// 15 struct InterpolateTableData(T) if( hasBasicMathOp!T ) { float key; T val; } 16 17 /// 18 auto lineInterpolate(T)( in InterpolateTableData!T[] tbl, float k, bool line_end=false ) 19 if( hasBasicMathOp!T ) 20 { 21 enforce( tbl.length > 1 ); 22 size_t i = tbl.length - find!"a.key > b"( tbl, k ).length; 23 if( !line_end ) 24 { 25 if( i <= 0 ) return tbl[0].val; 26 else if( i >= tbl.length ) return tbl[$-1].val; 27 } 28 else 29 { 30 if( i < 1 ) i = 1; 31 else if( i > tbl.length-1 ) i = tbl.length-1; 32 } 33 34 auto a = tbl[i-1]; 35 auto b = tbl[i]; 36 return cast(T)( a.val + ( b.val - a.val ) * ( ( k - a.key ) / ( b.key - a.key ) ) ); 37 } 38 39 /// 40 unittest 41 { 42 alias InterpolateTableData!float TT; 43 auto tbl = 44 [ 45 TT( 0, 10 ), 46 TT( 10, 18 ), 47 TT( 25, 20 ), 48 TT( 50, 13 ), 49 TT( 55, 25 ) 50 ]; 51 52 assertEq( lineInterpolate( tbl, 0 ), 10 ); 53 assertEq( lineInterpolate( tbl, 5 ), 14 ); 54 assertEq( lineInterpolate( tbl, 10 ), 18 ); 55 assertEq( lineInterpolate( tbl, -10 ), 10 ); 56 assertEq( lineInterpolate( tbl, 80 ), 25 ); 57 } 58 59 unittest 60 { 61 alias InterpolateTableData!double TD; 62 auto tbl = 63 [ 64 TD( 0, 0 ), 65 TD( 1, 1 ), 66 TD( 2, 3 ), 67 TD( 3, 4 ) 68 ]; 69 assertEq( lineInterpolate( tbl, 5, true ), 6 ); 70 assertEq( lineInterpolate( tbl, -3, true ), -3 ); 71 } 72 73 unittest 74 { 75 alias InterpolateTableData!vec3 TC; 76 auto tbl = 77 [ 78 TC( 0, vec3(1,0,0) ), 79 TC( 1, vec3(0,1,0) ), 80 TC( 2, vec3(0,0,1) ) 81 ]; 82 83 assertEq( lineInterpolate( tbl, -1 ), vec3(1,0,0) ); 84 assertEq( lineInterpolate( tbl, 0 ), vec3(1,0,0) ); 85 assertEq( lineInterpolate( tbl, 0.5 ), vec3(0.5,0.5,0) ); 86 assertEq( lineInterpolate( tbl, 3 ), vec3(0,0,1) ); 87 } 88 89 @property bool canBezierInterpolate(T,F)() 90 { return is( typeof( T.init * F.init + T.init * F.init ) == T ) && isNumeric!F; } 91 92 pure nothrow auto bezierInterpolation(T,F=float)( in T[] pts, F t ) 93 if( canBezierInterpolate!(T,F) ) 94 in 95 { 96 assert( t >= 0 && t <= 1 ); 97 assert( pts.length > 0 ); 98 } 99 body 100 { 101 auto N = pts.length-1; 102 auto omt = 1.0 - t; 103 T res = pts[0] * pow(omt,N) + pts[$-1] * pow(t,N); 104 foreach( i; 1 .. N ) 105 res = res + pts[i] * pow(t,i) * pow(omt,N-i) * combinationCount(N,i); 106 return res; 107 } 108 109 unittest 110 { 111 auto pts = [ vec2(0,0), vec2(2,2), vec2(4,0) ]; 112 assertEq( bezierInterpolation( pts, 0.5 ), vec2(2,1) ); 113 } 114 115 pure nothrow auto fixBezierSpline(T,F=float)( in T[] pts, size_t steps ) 116 { 117 auto step = cast(F)(1.0) / cast(F)(steps-1); 118 auto res = new T[](steps); 119 for( auto i=0; i < steps; i++ ) 120 res[i] = bezierInterpolation( pts, step * i ); 121 return res; 122 } 123 124 interface BezierSplineInterpolator(T,F=float) 125 if( canBezierInterpolate!(T,F) ) 126 { T[] opCall( in T[] ); } 127 128 class FixStepsBezierSplineInterpolator(T,F=float) : BezierSplineInterpolator!(T,F) 129 { 130 size_t steps; 131 this( size_t steps ) { this.steps = steps; } 132 133 T[] opCall( in T[] pts ) 134 { return fixBezierSpline!(T,F)( pts, steps ); } 135 } 136 137 unittest 138 { 139 enum size_t len = 100; 140 auto fbi = new FixStepsBezierSplineInterpolator!(vec2)(len); 141 auto pts = [ vec2(0,0), vec2(2,2), vec2(4,0) ]; 142 auto res = fbi( pts ); 143 assertEq( res.length, len ); 144 } 145 146 /+ функция критеря должна быть функцией хевисада +/ 147 auto criteriaBezierSpline(T,F=float)( in T[] pts, bool delegate(in T[], in T) criteria, F min_step=1e-5 ) 148 if( canBezierInterpolate!(T,F) ) 149 in 150 { 151 assert( pts.length > 1 ); 152 assert( criteria !is null ); 153 assert( min_step > 0 ); 154 assert( min_step < cast(F)(.25) / cast(F)(pts.length-1) ); 155 } 156 body 157 { 158 F step = cast(F)(.25) / cast(F)(pts.length-1); 159 160 auto ret = [ bezierInterpolation( pts, 0 ), bezierInterpolation( pts, min_step ) ]; 161 162 auto t = min_step; 163 auto o = step; 164 165 while( true ) 166 { 167 if( 1-t < o ) o = 1-t; 168 auto buf = bezierInterpolation( pts, t+o ); 169 170 if( criteria( ret, buf ) ) 171 { 172 t += o; 173 if( t >= 1 ) return ret ~ bezierInterpolation( pts, 1 ); 174 continue; 175 } 176 else 177 { 178 o /= 2.0; 179 if( o > min_step ) continue; 180 else t += o; 181 } 182 183 buf = bezierInterpolation( pts, t ); 184 185 if( t > 1 ) return ret ~ bezierInterpolation( pts, 1 ); 186 187 ret ~= buf; 188 o = step; 189 } 190 } 191 192 /+ функция критеря должна быть функцией хевисада +/ 193 auto filterBezierSpline(T,F=float)( in T[] pts, bool delegate(in T[], in T) criteria ) 194 if( canBezierInterpolate!(T,F) ) 195 in 196 { 197 assert( pts.length > 1 ); 198 assert( criteria !is null ); 199 } 200 body 201 { 202 auto ret = [ pts[0] ]; 203 204 for( auto i = 1; i < pts.length; i++ ) 205 { 206 while( i < pts.length && criteria(ret, pts[i]) ) i++; 207 208 if( ret[$-1] == pts[i-1] ) 209 ret ~= pts[i]; 210 else 211 ret ~= pts[i-1]; 212 } 213 214 if( ret[$-1] != pts[$-1] ) 215 ret ~= pts[$-1]; 216 217 return ret; 218 } 219 220 auto angleSplineCriteria(T)( float angle ) 221 if( isSpecVector!(2,float,T) || isSpecVector!(3,float,T) ) 222 { 223 auto cos_angle = cos( angle ); 224 return ( in T[] accepted, in T newpoint ) 225 { 226 if( accepted.length < 2 ) return false; 227 228 auto cc = [ accepted[$-2], accepted[$-1], newpoint ]; 229 230 auto a = (cc[1]-cc[0]).e; 231 auto b = (cc[2]-cc[1]).e; 232 233 return dot(a,b) >= cos_angle; 234 }; 235 } 236 237 auto lengthSplineCriteria(T)( float len ) 238 if( isSpecVector!(2,float,T) || isSpecVector!(3,float,T) ) 239 in{ assert( len > 0 ); } body 240 { 241 return ( in T[] accepted, in T newpoint ) 242 { return (accepted[$-1] - newpoint).len2 <= len*len; }; 243 } 244 245 unittest 246 { 247 auto pts = [ vec2(0,0), vec2(5,2), vec2(-1,2), vec2(4,0) ]; 248 auto pp = criteriaBezierSpline( pts, angleSplineCriteria!vec2(0.05) ); 249 250 assert( pp.length > pts.length ); 251 assertEq( pp[0], pts[0] ); 252 assertEq( pp[$-1], pts[$-1] ); 253 } 254 255 unittest 256 { 257 auto pts = [ vec2(0,0), vec2(5,2), vec2(-1,2), vec2(4,0) ]; 258 auto pp = filterBezierSpline( fixBezierSpline( pts, 1000 ), lengthSplineCriteria!vec2(0.05) ); 259 260 assert( pp.length > pts.length ); 261 assertEq( pp[0], pts[0] ); 262 assertEq( pp[$-1], pts[$-1] ); 263 }