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 }