1 module des.mc.calibrate.point; 2 3 public import std.math; 4 public import std.algorithm; 5 public import std.array; 6 public import des.math.linear; 7 8 import des.math.basic.traits; 9 10 import des.mc.calibrate.filter; 11 public import des.mc.calibrate.util; 12 13 class CalibratorException : Exception 14 { 15 this( string msg, string file=__FILE__, size_t line=__LINE__ ) 16 @safe pure nothrow 17 { super( msg, file, line ); } 18 } 19 20 enum bad_vec3 = vec3( float.nan, float.nan, float.nan ); 21 22 struct PointCalibrationResult 23 { 24 enum State 25 { 26 BUFFERED, 27 ACCEPTED, 28 ABORTED 29 } 30 31 State state; 32 fSeg avg_ray; 33 float quality; 34 float stability; 35 vec3 point = bad_vec3; 36 37 @property bool done() const { return !!point; } 38 } 39 40 struct PointCalibratorParam 41 { 42 size_t filter_length = 100; 43 float deviation_limit; 44 float min_quality = .5f; 45 float min_stability = .9f; 46 } 47 48 class PointCalibrator 49 { 50 protected: 51 PointCalibratorParam params; 52 53 FilterBuffer!fSeg fbuffer; 54 fSeg[] result_ray; 55 56 vec3 last_point = bad_vec3; 57 58 public: 59 60 this( PointCalibratorParam pcp ) 61 { 62 params = pcp; 63 fbuffer = new FilterBuffer!fSeg( params.filter_length ); 64 } 65 66 auto filter( in fSeg seg ) 67 { 68 fbuffer.append(seg); 69 auto dp = fbuffer.distributionParams; 70 auto avg = dp[0]; 71 auto var = dp[1]; 72 73 auto quality = calcQuality( result_ray ~ avg ); 74 auto stability = calcStability( var ); 75 76 auto result_state = PointCalibrationResult.State.BUFFERED; 77 78 if( checkStability(stability) ) 79 { 80 if( checkQuality(quality) ) 81 { 82 result_ray ~= avg; 83 fbuffer.reset(); 84 result_state = PointCalibrationResult.State.ACCEPTED; 85 } 86 else result_state = PointCalibrationResult.State.ABORTED; 87 } 88 89 last_point = calcPoint(); 90 return PointCalibrationResult( result_state, avg, quality, 91 stability, calcPoint() ); 92 } 93 94 @property auto point() const { return last_point; } 95 96 protected: 97 98 float calcQuality( in fSeg[] list ) const 99 { 100 if( list.length < 2 ) return 0; 101 float sum = 0; 102 foreach( i; 0 .. list.length-1 ) 103 foreach( j; i+1 .. list.length ) 104 { 105 auto a = list[i].dir.e; 106 auto b = list[j].dir.e; 107 auto alpha = acos(dot(a,b)); 108 auto norm = (cross(a, b)).e; 109 auto mv = list[i].start - list[j].start; 110 sum += sin(alpha) / ( 1.0f + abs(dot( norm, mv )) ); 111 } 112 return sum; 113 } 114 115 float calcStability( in fSeg variance ) const 116 { 117 auto p1 = normalizeDeviation( sqrt(variance.start.len) ); 118 auto p2 = normalizeDeviation( sqrt(variance.dir.len) ); 119 return ( 1.0f - ( p1 + p2 ) / 2.0f ) * fbuffer.fillRatio; 120 } 121 122 float normalizeDeviation( float deviation ) const 123 { 124 if( !isFinite(deviation) || deviation == 0 ) return 1; 125 return max( (deviation-params.deviation_limit) / deviation, 0 ); 126 } 127 128 bool checkQuality( float quality ) const 129 { return quality > params.min_quality || !result_ray.length; } 130 131 bool checkStability( float stability ) const 132 { return stability > params.min_stability && fbuffer.isFilled; } 133 134 bool isResultable() const { return result_ray.length > 1; } 135 136 auto calcPoint() const 137 { 138 if( !isResultable ) return bad_vec3; 139 140 vec3 res; 141 ulong k = 0; 142 143 foreach( i; 0 .. result_ray.length-1 ) 144 foreach( j; i+1 .. result_ray.length ) 145 { 146 k++; 147 auto ai = approxIntersection( result_ray[i], result_ray[j] ); 148 res += ai.start + ai.dir / 2.0f; 149 } 150 res /= k; 151 152 return res; 153 } 154 } 155 156 unittest 157 { 158 auto pc = new PointCalibrator( PointCalibratorParam(20, 0.15) ); 159 160 auto r1 = fSeg( vec3(0,0,0), vec3(1,1,0) ); 161 auto r2 = fSeg( vec3(1,0,-1), vec3(-4,4,0) ); 162 163 PointCalibrationResult q; 164 165 q = pc.filter( r1 + rndSeg() ); 166 167 assert( q.state == PointCalibrationResult.State.BUFFERED ); 168 169 while( q.state != PointCalibrationResult.State.ACCEPTED ) 170 q = pc.filter( r1 + rndSeg() ); 171 172 assert( !q.done ); 173 174 q.state = PointCalibrationResult.State.BUFFERED; 175 176 while( q.state != PointCalibrationResult.State.ACCEPTED ) 177 q = pc.filter( r2 + rndSeg() ); 178 179 assert( q.point ); 180 assert( q.point == pc.point ); 181 assert( (pc.point - vec3(.5,.5,-.5)).len2 < 0.1 ); 182 } 183 184 unittest 185 { 186 auto pc = new PointCalibrator( PointCalibratorParam(20, 0.15) ); 187 auto r1 = fSeg( vec3(0,0,0), vec3(1,1,0) ); 188 PointCalibrationResult q; 189 while( q.state != PointCalibrationResult.State.ACCEPTED ) 190 q = pc.filter( r1 + rndSeg() ); 191 192 while( q.state != PointCalibrationResult.State.ABORTED ) 193 { 194 q = pc.filter( r1 + rndSeg() ); 195 assert( q.quality < 0.1 ); 196 } 197 assert( q.state == PointCalibrationResult.State.ABORTED ); 198 }