inline std::tuple find_PC_PathLength(const TVector3& x1, const TVector3& x2) { /* Square-aperture detector geometry replacing the original circular one-sheet hyperboloid. The active surfaces are centered on the z axis and form squares rather than circles: - anodes: 40 mm x 40 mm - cathodes: 42 mm x 42 mm The beam is kept on-axis, so the square openings enclose the beam while still defining the anode/cathode boundaries in the x-y plane. */ /* // Original circular hyperboloid implementation retained for reference. // This section is intentionally commented out to avoid using the circular geometry. TVector3 dx = x2-x1; // direction vector double t2 = 1.0; //The value of 't' at the destination point, by definition: t=(z(t)-z0)/dz auto onesheet_hyperboloid_intersect = [&](double a, double c) { auto A = pow(dx.Perp(),2)/(a*a) - pow(dx.Z(),2)/(c*c); auto B = 2*(dx.X()*x1.X()+dx.Y()*x1.Y())/(a*a) - 2*(dx.Z()*x1.Z())/(c*c); auto C = pow(x1.Perp(),2)/(a*a) - pow(x1.Z(),2)/(c*c) - 1.0; double disc = B*B - 4*A*C; if(disc<0) return TVector3(0,0,54321); else { double tsol1 = (-B + TMath::Sqrt(disc))/(2*A); double tsol2 = (-B - TMath::Sqrt(disc))/(2*A); if(tsol1 >= 0 && tsol1 <= t2) return x1+tsol1*dx; else if(tsol2>=0 && tsol2 <= t2) return x1+tsol2*dx; else return TVector3(0,0,54321); } }; TVector3 anode_intersect = onesheet_hyperboloid_intersect(32.0429,301.895); TVector3 cathode_intersect = onesheet_hyperboloid_intersect(37.239045,301.895); TVector3 gw_intersect = onesheet_hyperboloid_intersect(27.712,301.895); if(anode_intersect.Z()!=54321 && cathode_intersect.Z()!=54321 && gw_intersect.Z()!=54321) return std::tuple(cathode_intersect,gw_intersect,(cathode_intersect-gw_intersect).Mag()*0.1); else return std::tuple(TVector3(0,0,0), TVector3(0,0,0), 54321); */ const double anode_half_width = 20.0; // 40 mm square side length const double cathode_half_width = 21.0; // 42 mm square side length const double guard_half_width = 18.0; // square guard opening centered on beam axis, still encloses beam const double z_start = -100.0; // square detector opening begins here along the beam axis const double invalid_z = 54321.0; const double eps = 1.0e-12; TVector3 dx = x2 - x1; auto square_intersection = [&](const TVector3& start, const TVector3& direction, double half_width) { double best_t = 1.0; bool found = false; auto consider_t = [&](double t, bool x_hit, bool y_hit) { if (t < -eps || t > 1.0 + eps) return; TVector3 point = start + t * direction; double abs_x = std::abs(point.X()); double abs_y = std::abs(point.Y()); bool inside_square = (abs_x <= half_width + eps) && (abs_y <= half_width + eps); bool on_boundary = (x_hit && std::abs(abs_x - half_width) <= eps) || (y_hit && std::abs(abs_y - half_width) <= eps); bool beyond_start = point.Z() >= z_start - eps; if (inside_square && on_boundary && beyond_start && t >= 0.0 && t <= 1.0) { if (!found || t < best_t) { best_t = t; found = true; } } }; if (std::abs(direction.X()) > eps) { double t_plus_x = (half_width - start.X()) / direction.X(); double t_minus_x = (-half_width - start.X()) / direction.X(); consider_t(t_plus_x, true, false); consider_t(t_minus_x, true, false); } if (std::abs(direction.Y()) > eps) { double t_plus_y = (half_width - start.Y()) / direction.Y(); double t_minus_y = (-half_width - start.Y()) / direction.Y(); consider_t(t_plus_y, false, true); consider_t(t_minus_y, false, true); } if (!found) { return TVector3(0, 0, invalid_z); } return start + best_t * direction; }; TVector3 anode_intersect = square_intersection(x1, dx, anode_half_width); TVector3 cathode_intersect = square_intersection(x1, dx, cathode_half_width); TVector3 gw_intersect = square_intersection(x1, dx, guard_half_width); if (anode_intersect.Z() != invalid_z && cathode_intersect.Z() != invalid_z && gw_intersect.Z() != invalid_z) return std::tuple(cathode_intersect, gw_intersect, (cathode_intersect - gw_intersect).Mag() * 0.1); else return std::tuple(TVector3(0, 0, 0), TVector3(0, 0, 0), invalid_z); }