@@ -24,11 +24,19 @@ namespace KinKal {
2424 Intersection retval;
2525 double ttest = tdir == TimeDir::forwards ? trange.begin () : trange.end ();
2626 auto pos = ktraj.position3 (ttest);
27- double speed = ktraj.speed (ttest); // speed is constant
27+ double rbend = ktraj.bendRadius ();
28+ double tstep = trange.range ();
29+ double speed = ktraj.speed ();
30+ // if there's curvature, take smaller steps
31+ if (rbend > 0.0 ){
32+ double tlenmax = 2.0 *sqrt (2.0 *rbend*tol); // maximum transverse length keeping the sagitta within tolerance
33+ double tspeed = ktraj.transverseSpeed ();
34+ tstep = std::min (tstep,(tlenmax+tol)/tspeed); // trajectory range defines maximum step
35+ }
36+ // sign!
37+ tstep *= timeDirSign (tdir);
2838 bool startinside = surf.isInside (pos);
2939 bool stepinside;
30- // set the step according to the range and tolerance. The maximum range division is arbitrary, it should be set to a physical value TODO
31- double tstep = timeDirSign (tdir)*std::max (0.05 *trange.range (),tol/speed); // trajectory range defines maximum step
3240 unsigned niter (0 );
3341 // step until we cross the surface or the time is out of range
3442 do {
@@ -82,8 +90,8 @@ namespace KinKal {
8290 retval.norm_ = surf.normal (retval.pos_ );
8391 }
8492 }
85- // check the final time to be in range; if we're out of range, negate the intersection
86- if (! trange.inRange (retval.time_ ))retval. inbounds_ = false ; // I should make a separate flag for time bounds TODO
93+ // check the final time to be in range
94+ retval. inrange_ = trange.inRange (retval.time_ );
8795 return retval;
8896 }
8997 //
@@ -117,10 +125,13 @@ namespace KinKal {
117125
118126 template < class HELIX > Intersection hcIntersect ( HELIX const & helix, KinKal::Cylinder const & cyl, TimeRange trange ,double tol,TimeDir tdir = TimeDir::forwards) {
119127 Intersection retval;
128+ // test if the range is lnger than the radius. If so, make an axial approximation
129+ double rbend = fabs (helix.bendRadius ());
120130 // compare directions and divide into cases
121131 double ddot = fabs (helix.bnom ().Unit ().Dot (cyl.axis ()));
122- if (ddot > 0.9 ) { // I need a more physical co-linear test TODO
123- // the helix and cylinder are roughly co-linear.
132+ if (rbend < trange.range ()*helix.transverseSpeed () && ddot > 0.9 ) { // mostly co-linear
133+ // the helix and cylinder are roughly co-linear, and the helix loops within this range
134+ // first try to limit the range using disk-axis intersections
124135 // see if the range is in bounds
125136 auto binb = cyl.inBounds (helix.position3 (trange.begin ()),tol);
126137 auto einb = cyl.inBounds (helix.position3 (trange.end ()),tol);
@@ -150,57 +161,56 @@ namespace KinKal {
150161 }
151162 }
152163 }
153- // } else if (ddot < 0.1) {
154- // // the helix and cylinder are mostly orthogonal, use POCA to the axis to find an initial estimate, then do a linear search
155- // // TODO. Construct a KinematicLine object from the helix axis, and a GeometricLine from the cylinder, then invoke POCA.
156- } else {
157- // intermediate case: use step intersection
158- retval = stepIntersect (helix,cyl,trange,tol,tdir);
159- }
160- return retval;
164+ } else {
165+ // intermediate case: use step intersection
166+ retval = stepIntersect (helix,cyl,trange,tol,tdir);
167+ }
168+ return retval;
161169 }
162170 //
163171 // Helix and planar surfaces
164172 //
165173 template <class HELIX > Intersection hpIntersect ( HELIX const & helix, KinKal::Plane const & plane, TimeRange trange ,double tol,TimeDir tdir = TimeDir::forwards) {
166174 Intersection retval;
167- double tstart = tdir == TimeDir::forwards ? trange.begin () : trange.end ();
168- auto axis = helix.axis (tstart);
169- if (tdir == TimeDir::backwards)axis.reverse ();
170- // test for the helix being circular or tangent to the plane
171- double vz = helix.axisSpeed (); // speed along the helix axis
172- double ddot = fabs (axis.direction ().Dot (plane.normal ()));
173- double zrange = fabs (vz*trange.range ());
174- if (zrange > tol && ddot > tol/zrange ){
175- // Find the intersection time of the helix axis (along bnom) with the plane
176- double dist (0.0 );
177- auto pinter = plane.intersect (axis,dist,true ,tol);
178- if (pinter.onsurface_ ){
179- // translate the axis intersection to a time
180- double tmid = tstart + timeDirSign (tdir)*dist/vz;
181- // bound the range of intersections by the extrema of the cylinder-plane intersection
182- double tantheta = sqrt (std::max (0.0 ,1.0 -ddot*ddot))/ddot;
183- double dt = std::max (tol/vz,helix.bendRadius ()*tantheta/vz); // make range finite in case the helix is exactly co-linear with the plane normal
184- // if we're already in tolerance, finish
185- if (dt*vz/ddot < tol){
186- retval.onsurface_ = pinter.onsurface_ ;
187- retval.inbounds_ = pinter.inbounds_ ;
188- retval.time_ = tmid;
189- retval.pos_ = helix.position3 (tmid);
190- retval.pdir_ = helix.direction (tmid);
191- retval.norm_ = plane.normal (retval.pos_ );
192- } else {
175+ // test if the range is lnger than the radius. If so, make an axial approximation
176+ double rbend = fabs (helix.bendRadius ());
177+ if (rbend > trange.range ()*helix.transverseSpeed ()){
178+ retval = stepIntersect (helix,plane,trange,tol,tdir);
179+ } else {
180+ // the trajectory curves macroscopically over this range: see if the axis is normal
181+ double tstart = tdir == TimeDir::forwards ? trange.begin () : trange.end ();
182+ auto ray = helix.linearize (tstart);
183+ auto velo = helix.velocity (tstart);
184+ if (tdir == TimeDir::backwards) ray.reverse (); // reverse if going backwards in time
185+ double vax = velo.Dot (ray.direction ()); // speed along axis
186+ // test for the helix being circular or tangent to the plane
187+ double ddot = fabs (ray.direction ().Dot (plane.normal ()));
188+ double zrange = fabs (vax*trange.range ());
189+ if (zrange > tol && ddot > tol/zrange ){
190+ // Find the intersection time of the helix ray (along bnom) with the plane to reduce the range
191+ double dist (0.0 );
192+ auto pinter = plane.intersect (ray,dist,true ,tol);
193+ if (pinter.onsurface_ ){
194+ // translate the ray intersection distance to a time
195+ double tmid = tstart + dist/vax;
196+ // bound the range of intersections by the extrema of the cylinder-plane intersection
197+ double tantheta = sqrt (std::max (1.0 -ddot*ddot,0.0 ))/ddot;
198+ // distance along axis to the surface to bound the reduced range; add tolerance
199+ double dd = tol+rbend*tantheta;
200+ // intersection should be bounded by the
201+ auto dt = fabs (dd/vax);
193202 TimeRange srange (tmid-dt,tmid+dt);
203+ // if this range overlaps with the original, compute the intersection
194204 if (srange.restrict (trange)){
195205 // step to the intersection in the restricted range. Use a separate intersection object as the
196206 // range is different
197207 retval = stepIntersect (helix,plane,srange,tol,tdir);
198208 }
199209 }
210+ } else {
211+ // simply step to intersection
212+ retval = stepIntersect (helix,plane,trange,tol,tdir);
200213 }
201- } else {
202- // simply step to intersection
203- retval = stepIntersect (helix,plane,trange,tol,tdir);
204214 }
205215 return retval;
206216 }
@@ -291,8 +301,8 @@ namespace KinKal {
291301 // calculate the time
292302 retval.time_ = tstart + dist*timeDirSign (tdir)/kkline.speed (tstart);
293303 }
294- // check the final time to be in range; if we're out of range, negate the intersection
295- if (! trange.inRange (retval.time_ ))retval. inbounds_ = false ; // I should make a separate flag for time bounds TODO
304+ // check the final time to be in range;
305+ retval. inrange_ = trange.inRange (retval.time_ );
296306 return retval;
297307 }
298308 // generic surface intersection cast down till we find something that works. This will only be used for helices, as KinematicLine
0 commit comments