1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.lcsim.fit.twopointcircle;
24
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.lcsim.event.TrackerHit;
29
30
31
32
33 public class TwoPointCircleFitter {
34 private double _rmin;
35 private List<TwoPointCircleFit> _circlefits;
36 private TwoPointLineFit _linefit;
37 private boolean _debug = false;
38 private static double _eps = 1e-6;
39 private static double twopi = 2. * Math.PI;
40
41
42
43
44
45
46
47
48 public TwoPointCircleFitter(double rmin) {
49 _rmin = rmin;
50 _circlefits = new ArrayList<TwoPointCircleFit>();
51 }
52
53
54
55
56 public TwoPointCircleFitter() {
57 this(0.);
58 }
59
60
61
62
63
64
65
66
67
68 public boolean FitCircle(TrackerHit hit1, TrackerHit hit2, double dmax) {
69 double[] pos1 = hit1.getPosition();
70 double[] pos2 = hit2.getPosition();
71 return FitCircle(pos1[0], pos1[1], pos2[0], pos2[1], dmax);
72 }
73
74
75
76
77
78
79
80
81
82
83
84 public boolean FitCircle(double x1, double y1, double x2, double y2, double dmax) {
85
86
87 _circlefits.clear();
88 _linefit = null;
89
90
91 double r1sq = x1*x1 + y1*y1;
92 double r2sq = x2*x2 + y2*y2;
93 double dmaxsq = dmax*dmax;
94
95
96
97 if (r1sq < dmaxsq || r2sq < dmaxsq)
98 throw new RuntimeException("Cannot handle case where hit is inside maximum impact parameter cut");
99
100
101 double ux = x2 - x1;
102 double uy = y2 - y1;
103 double u = Math.sqrt(ux*ux + uy*uy);
104 ux = ux / u;
105 uy = uy / u;
106 if (u<_eps)return false;
107
108
109 double mx = 0.5 * (x1 + x2);
110 double my = 0.5 * (y1 + y2);
111 double msq = mx*mx + my*my;
112
113
114 double vx = uy;
115 double vy = -ux;
116
117
118 double ipinf = (x1*y2 - y1*x2) / u;
119
120
121 int nalpha = 2;
122 double alpha[] = new double[nalpha];
123
124
125 double denom = 2. * (ipinf*ipinf - dmaxsq);
126
127
128 boolean sltrack = denom < _eps;
129
130
131
132 if (Math.abs(denom) < _eps) {
133
134
135 double beta = msq - 0.25 * u*u - dmaxsq;
136 nalpha = 1;
137 alpha[0] = (u*u * dmaxsq - beta*beta) / (4. * beta * ipinf);
138
139 } else {
140
141
142 double r1dotr2 = x1*x2 + y1*y2;
143 double term1 = -ipinf * (r1dotr2 - dmaxsq) / denom;
144 double term2 = Math.abs(dmax * Math.sqrt((r1sq - dmaxsq) * (r2sq - dmaxsq)) / denom);
145
146
147 if (term1 < 0.) term2 = -term2;
148 alpha[0] = term1 + term2;
149 alpha[1] = term1 - term2;
150 }
151
152
153 for (int i = 0; i<nalpha; i++) {
154
155
156 double rcurv = Math.sqrt(alpha[i]*alpha[i] + 0.25 * u*u);
157 if (rcurv < _rmin) {
158
159
160
161 if (i == 0 && !sltrack) return false;
162
163
164
165 rcurv = _rmin;
166 double newalpha = Math.sqrt(_rmin*_rmin - 0.25 * u*u);
167
168
169
170
171
172
173 int isign = 1;
174 if (i == 1 && sltrack && alpha[0] >= 0.) isign = -1;
175 if (i == 1 && !sltrack && alpha[0] < 0.) isign = -1;
176 alpha[i] = isign * newalpha;
177 }
178
179
180 double xc = mx + alpha[i] * vx;
181 double yc = my + alpha[i] * vy;
182 double rc = Math.sqrt(xc*xc + yc*yc);
183
184
185 double x0 = xc * (1. - rcurv / rc);
186 double y0 = yc * (1. - rcurv / rc);
187
188
189 if (_debug) {
190 double c1 = Math.sqrt((x1-xc)*(x1-xc)+(y1-yc)*(y1-yc));
191 if (Math.abs(c1-rcurv) > _eps * c1) throw new RuntimeException("Error in circle finding - c1 = "+c1+" rcurv = "+rcurv);
192 double c2 = Math.sqrt((x2-xc)*(x2-xc)+(y2-yc)*(y2-yc));
193 if (Math.abs(c2-rcurv) > _eps * c2) throw new RuntimeException("Error in circle finding - c2 = "+c2+" rcurv = "+rcurv);
194 double c3 = Math.sqrt((x0-xc)*(x0-xc)+(y0-yc)*(y0-yc));
195 if (Math.abs(c3-rcurv) > _eps * c3) throw new RuntimeException("Error in circle finding - c3 = "+c3+" rcurv = "+rcurv);
196 double r0 = Math.sqrt(x0*x0 + y0*y0);
197 if (r0 > dmax+100*_eps) throw new RuntimeException("Invalid DCA point for solution "+i+" r0: "+r0+" dmax: "+dmax);
198 if (rcurv < _rmin) throw new RuntimeException("Invalid circle radius for solution "+i+" rcurv: "+rcurv+" rmin: "+_rmin);
199 }
200
201
202
203 double phi0 = Math.atan2(y0-yc, x0-xc);
204 double phi1 = Math.atan2(y1-yc, x1-xc);
205 double phi2 = Math.atan2(y2-yc, x2-xc);
206
207
208 double dphi1 = phi1 - phi0;
209 if (dphi1 > Math.PI) dphi1 -= twopi;
210 if (dphi1 < -Math.PI) dphi1 += twopi;
211 double dphi2 = phi2 - phi0;
212 if (dphi2 > Math.PI) dphi2 -= twopi;
213 if (dphi2 < -Math.PI) dphi2 += twopi;
214
215
216 boolean cw;
217 if (Math.abs(dphi1) < Math.abs(dphi2)) cw = dphi1 < 0.;
218 else cw = dphi2 < 0.;
219
220
221 double s1 = -dphi1 * rcurv;
222 double s2 = -dphi2 * rcurv;
223 if (!cw) {
224 s1 = -s1;
225 s2 = -s2;
226 }
227
228
229 if (s1 < 0.) s1 += twopi * rcurv;
230 if (s2 < 0.) s2 += twopi * rcurv;
231
232 _circlefits.add(new TwoPointCircleFit(xc, yc, rcurv, cw, s1, s2));
233 }
234
235
236
237 if (sltrack) {
238
239
240 double s1 = (x1*ux + y1*uy);
241 double s2 = (x2*ux + y2*uy);
242
243
244 if (s1*s2 < 0.) {
245
246
247 double x0 = x1 - s1 * ux;
248 double y0 = y1 - s1 * uy;
249 double phi = Math.atan2(uy, ux);
250
251
252 if (s1 < 0.) {
253 s1 = -s1;
254 s2 = -s2;
255 phi = phi + Math.PI;
256 if (phi > Math.PI) phi += twopi;
257 }
258
259
260 _linefit = new TwoPointLineFit(x0, y0, phi, s1, s2);
261 }
262
263
264 if (_debug & _circlefits.size() == 0) throw new RuntimeException("No circle found for hits consistent with infinite momentum");
265 }
266
267 return _circlefits.size() > 0;
268 }
269
270
271
272
273
274
275 public List<TwoPointCircleFit> getCircleFits() {
276 return _circlefits;
277 }
278
279
280
281
282
283
284
285
286
287 public TwoPointLineFit getLineFit() {
288 return _linefit;
289 }
290
291
292
293
294
295
296 public void setRMin(double rmin) {
297 _rmin = rmin;
298 }
299
300
301
302
303
304
305 public void setDebug(boolean debug) {
306 _debug = debug;
307 }
308 }