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