1 module des.mc.calibrate.multipoint;
2 
3 import des.mc.calibrate.point;
4 import des.mc.calibrate.util;
5 
6 float toRad( float deg ) pure nothrow { return deg / 180.0 * PI; }
7 
8 interface MultiPointCalibrator
9 {
10     void reset();
11     void nextPoint();
12     PointCalibrationResult filter( in fSeg seg );
13 
14     const @property
15     {
16         const(vec3)[] points();
17         bool done();
18         size_t currentIndex();
19     }
20 }
21 
22 class BaseMultiPointCalibrator : MultiPointCalibrator
23 {
24 protected:
25     PointCalibrator[] calibrators;
26     size_t current;
27     PointCalibratorParam calibrator_params;
28 
29 public:
30     this( size_t point_count, PointCalibratorParam pcp )
31     {
32         calibrator_params = pcp;
33         foreach( i; 0 .. point_count )
34             calibrators ~= new PointCalibrator( pcp );
35     }
36 
37     void reset()
38     {
39         foreach( pc; calibrators )
40             pc = new PointCalibrator( calibrator_params );
41     }
42 
43     void nextPoint() { current = ( current + 1 ) % calibrators.length; }
44 
45     PointCalibrationResult filter( in fSeg seg )
46     { return calibrators[current].filter( seg ); }
47 
48     const @property
49     {
50         const(vec3)[] points() { return array( map!(a=>a.point)( calibrators ) ); }
51         bool done() { return all(points); }
52         size_t currentIndex() { return current; }
53     }
54 }
55 
56 unittest
57 {
58     fSeg[] rays = 
59         [
60         fSeg( vec3(0,0,-0.1), vec3(1,1,0) ), fSeg( vec3(2,0,0.1), vec3(-1,1,0) ),
61         fSeg( vec3(0,3,0), vec3(2,1,0) ), fSeg( vec3(0,4,0), vec3(1,0,0) ),
62         ];
63 
64     size_t cur_ray = 0;
65 
66     vec3[] res = [ vec3(1,1,0), vec3(2,4,0) ];
67 
68     auto bmpc = new BaseMultiPointCalibrator( 2, PointCalibratorParam(10,0.15,0.4,0.9) );
69 
70     bool normalCalcFirstPoint = false;
71     foreach( i; 0 .. 20 )
72     {
73         auto q = bmpc.filter( rays[cur_ray] + rndSeg() );
74         if( q.state == PointCalibrationResult.State.ACCEPTED )
75         {
76             assert( abs( i - 10 ) < 2 );
77             normalCalcFirstPoint = true;
78             break;
79         }
80     }
81     assert(normalCalcFirstPoint);
82 
83     void appendNextRay()
84     {
85         cur_ray++;
86         foreach( i; 0 .. 20 )
87         {
88             auto yx = bmpc.filter( rays[cur_ray] + rndSeg() );
89             if( yx.state == PointCalibrationResult.State.ACCEPTED ) break;
90         }
91     }
92 
93     appendNextRay();
94     assert( !bmpc.done );
95     assert( bmpc.currentIndex == 0 );
96     bmpc.nextPoint();
97     assert( bmpc.currentIndex == 1 );
98     appendNextRay();
99     assert( !bmpc.done );
100     appendNextRay();
101     assert( bmpc.done );
102     import std.range;
103     assert( all!(a=>a.len2<0.1)( map!(a=>a[0]-a[1])( zip(bmpc.points, res) ) ) );
104 }
105 
106 interface MultiPointCalibratorPrinter
107 {
108     void nextPoint();
109     void accepted(size_t,PointCalibrationResult);
110     void aborted(size_t,PointCalibrationResult);
111     void done(in vec3[]);
112 }
113 
114 class CBMultiPointCalibrator : BaseMultiPointCalibrator
115 {
116     MultiPointCalibratorPrinter printer;
117 
118     this( size_t point_count, in PointCalibratorParam pcp,
119             MultiPointCalibratorPrinter prntr=null ) 
120     {
121         super( point_count, pcp );
122         printer = prntr;
123     }
124 
125     override PointCalibrationResult filter( in fSeg seg )
126     {
127         auto res = super.filter( seg );
128         if( printer )
129         {
130             if( res.state == PointCalibrationResult.State.ACCEPTED )
131                 printer.accepted( currentIndex, res );
132             if( res.state == PointCalibrationResult.State.ABORTED ) 
133                 printer.aborted( currentIndex, res );
134             if( done ) printer.done( points );
135         }
136         return res;
137     }
138 
139     override void nextPoint()
140     {
141         super.nextPoint();
142         if( printer ) printer.nextPoint();
143     }
144 }
145 
146 abstract class BehaviorMultiPointCalibrator : MultiPointCalibrator
147 {
148 protected:
149     MultiPointCalibrator calibrator;
150 
151 public:
152     this( MultiPointCalibrator mpc ) { calibrator = mpc; }
153 
154     void reset() { calibrator.reset(); }
155     void nextPoint() { calibrator.nextPoint(); }
156 
157     const @property
158     {
159         const(vec3)[] points() { return calibrator.points; }
160         bool done() { return calibrator.done; }
161         size_t currentIndex() { return calibrator.currentIndex; }
162     }
163 }
164 
165 /+ калибровка каждой точки по отдельности +/
166 class EachMultiPointCalibrator : BehaviorMultiPointCalibrator
167 {
168     this( MultiPointCalibrator mpc ) { super(mpc); }
169 
170     auto filter( in fSeg seg )
171     {
172         auto res = calibrator.filter( seg );
173         if( res.done ) nextPoint();
174         return res;
175     }
176 }
177 
178 unittest
179 {
180     fSeg[] rays = 
181         [
182         fSeg( vec3(0,0,-0.1), vec3(1,1,0) ), fSeg( vec3(2,0,0.1), vec3(-1,1,0) ),
183         fSeg( vec3(0,3,0), vec3(2,1,0) ), fSeg( vec3(0,4,0), vec3(1,0,0) ),
184         ];
185 
186     size_t cur_ray = 0;
187 
188     vec3[] res = [ vec3(1,1,0), vec3(2,4,0) ];
189 
190     size_t next_point_call_count = 0;
191 
192     class TestPrinter : MultiPointCalibratorPrinter
193     {
194         void nextPoint() { next_point_call_count++; }
195         void accepted(size_t,PointCalibrationResult) { cur_ray++; }
196         void aborted(size_t,PointCalibrationResult) {}
197         void done(in vec3[]) {}
198     }
199 
200     auto buf_len = 10;
201     auto cbmpc = new CBMultiPointCalibrator( 2, PointCalibratorParam(buf_len,0.15,0.4), new TestPrinter );
202     auto eachmp = new EachMultiPointCalibrator( cbmpc );
203 
204     size_t steps = 0;
205     while( !eachmp.done )
206     {
207         steps++;
208         eachmp.filter( rays[cur_ray]+rndSeg() );
209     }
210     assert( abs( steps - buf_len * 4 ) < 3 );
211     assert( next_point_call_count == 2 );
212     import std.range;
213     assert( all!(a=>a.len2<0.1)( map!(a=>a[0]-a[1])( zip(eachmp.points, res) ) ) );
214 }
215 
216 /+ калибровка серии точек из одного положения +/
217 class SeriesMultiPointCalibrator : BehaviorMultiPointCalibrator
218 {
219 protected:
220     float min_next_point_angle;
221 
222 public:
223     this( MultiPointCalibrator mpc, float mnpa )
224     {
225         super(mpc);
226         min_next_point_angle = abs(mnpa);
227     }
228 
229     override auto filter( in fSeg seg )
230     {
231         auto res = calibrator.filter( seg );
232         if( res.state != PointCalibrationResult.State.BUFFERED && 
233                 checkRay( res.avg_ray ) ) nextPoint();
234         return res;
235     }
236 
237 protected:
238 
239     fSeg last_ray;
240 
241     bool checkRay( in fSeg ray )
242     {
243         if( last_ray.dir.len == 0 )
244         {
245             last_ray = ray;
246             return true;
247         }
248 
249         auto a = last_ray.dir.e;
250         auto b = ray.dir.e;
251         return acos(dot( a, b )) > min_next_point_angle;
252     }
253 }