19double Interpolate(
const std::vector<double>& y,
20 const std::vector<double>& x,
const double xx) {
22 const double tol = 1.e-6 *
fabs(
x.back() -
x.front());
23 if (xx <
x.front())
return y.front();
24 const auto it1 = std::upper_bound(
x.cbegin(),
x.cend(), xx);
25 if (it1 ==
x.cend())
return y.back();
26 const auto it0 = std::prev(it1);
27 const double dx = (*it1 - *it0);
28 if (dx < tol)
return y[it0 -
x.cbegin()];
29 const double f = (xx - *it0) / dx;
30 return y[it0 -
x.cbegin()] * (1. - f) + f * y[it1 -
x.cbegin()];
33bool OnLine(
const double x1,
const double y1,
const double x2,
const double y2,
34 const double u,
const double v) {
36 double epsx = 1.e-10 * std::max({
fabs(x1),
fabs(x2),
fabs(u)});
37 double epsy = 1.e-10 * std::max({
fabs(y1),
fabs(y2),
fabs(v)});
38 epsx = std::max(1.e-10, epsx);
39 epsy = std::max(1.e-10, epsy);
41 if ((
fabs(x1 - u) <= epsx &&
fabs(y1 - v) <= epsy) ||
42 (
fabs(x2 - u) <= epsx &&
fabs(y2 - v) <= epsy)) {
45 }
else if (
fabs(x1 - x2) <= epsx &&
fabs(y1 - y2) <= epsy) {
49 double xc = 0., yc = 0.;
52 const double dx = (x2 - x1);
53 const double dy = (y2 - y1);
54 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
67 const double dx = (x1 - x2);
68 const double dy = (y1 - y2);
69 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
82 if (
fabs(u - xc) < epsx &&
fabs(v - yc) < epsy) {
88bool Crossing(
const double x1,
const double y1,
const double x2,
89 const double y2,
const double u1,
const double v1,
90 const double u2,
const double v2) {
93 if (OnLine(x1, y1, x2, y2, u1, v1) || OnLine(x1, y1, x2, y2, u2, v2) ||
94 OnLine(u1, v1, u2, v2, x1, y1) || OnLine(u1, v1, u2, v2, x2, y2)) {
98 std::array<std::array<double, 2>, 2> a;
103 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
107 epsx = std::max(epsx, 1.e-10);
108 epsy = std::max(epsy, 1.e-10);
109 if (
fabs(det) < epsx * epsy) {
114 const double aux = a[1][1];
115 a[1][1] = a[0][0] / det;
117 a[1][0] = -a[1][0] / det;
118 a[0][1] = -a[0][1] / det;
120 const double xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
121 const double yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
123 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
139 std::cerr <<
m_className <<
"::SetSensor: Null pointer.\n";
144 m_component =
nullptr;
149 std::cerr <<
m_className <<
"::SetComponent: Null pointer.\n";
160 std::cerr <<
m_className <<
"::SetAspectRatioSwitch: Value must be > 0.\n";
168 if (thr < 0. || thr > 1.) {
169 std::cerr <<
m_className <<
"::SetLoopThreshold:\n"
170 <<
" Value must be between 0 and 1.\n";
173 m_loopThreshold = thr;
178 if (thr < 0. || thr > 1.) {
179 std::cerr <<
m_className <<
"::SetConnectionThreshold:\n"
180 <<
" Value must be between 0 and 1.\n";
183 m_connectionThreshold = thr;
187 const std::vector<std::array<double, 3> >& points,
const bool rev,
188 const bool colour,
const bool markers,
const bool plotDriftLines) {
190 if (!m_sensor && !m_component) {
192 <<
" Neither sensor nor component are defined.\n";
196 std::cerr <<
m_className <<
"::PlotIsochrons: Time step must be > 0.\n";
199 if (points.empty()) {
201 <<
" No starting points provided.\n";
204 if (!SetPlotLimits())
return;
207 canvas->SetTitle(
"Isochrons");
210 frame->GetXaxis()->SetTitle(
LabelX().c_str());
211 frame->GetYaxis()->SetTitle(
LabelY().c_str());
218 std::vector<std::vector<std::array<double, 3> > > driftLines;
219 std::vector<std::array<double, 3> > startPoints;
220 std::vector<std::array<double, 3> > endPoints;
221 std::vector<int> statusCodes;
223 ComputeDriftLines(tstep, points, driftLines, startPoints, endPoints,
225 const unsigned int nDriftLines = driftLines.size();
226 if (nDriftLines < 2) {
227 std::cerr <<
m_className <<
"::PlotIsochrons: Too few drift lines.\n";
231 std::size_t nContours = 0;
232 for (
const auto& driftLine : driftLines) {
233 nContours = std::max(nContours, driftLine.size());
235 if (nContours == 0) {
236 std::cerr <<
m_className <<
"::PlotIsochrons: No contour lines.\n";
240 std::set<int> allStats;
241 for (
const auto stat : statusCodes) allStats.insert(stat);
246 <<
" Drawing " << nContours <<
" contours, "
247 << nDriftLines <<
" drift lines.\n";
248 std::printf(
" Connection threshold: %10.3f\n", m_connectionThreshold);
249 std::printf(
" Aspect ratio threshold: %10.3f\n", m_aspectRatio);
250 std::printf(
" Loop closing threshold: %10.3f\n", m_loopThreshold);
251 if (m_sortContours) {
252 std::cout <<
" Sort contours.\n";
254 std::cout <<
" Do not sort contours.\n";
256 if (m_checkCrossings) {
257 std::cout <<
" Check for crossings.\n";
259 std::cout <<
" Do not check for crossings.\n";
262 std::cout <<
" Mark isochron points.\n";
264 std::cout <<
" Draw isochron lines.\n";
270 graph.SetLineColor(kGray + 2);
271 graph.SetMarkerColor(kGray + 2);
272 graph.SetLineStyle(m_lineStyle);
273 graph.SetMarkerStyle(m_markerStyle);
274 const double colRange = double(gStyle->GetNumberOfColors()) / nContours;
275 for (
unsigned int ic = 0; ic < nContours; ++ic) {
277 const auto col = gStyle->GetColorPalette(
int((ic + 0.99) * colRange));
278 graph.SetLineColor(col);
279 graph.SetMarkerColor(col);
281 for (
int stat : allStats) {
282 std::vector<std::pair<std::array<double, 4>,
unsigned int> > contour;
284 for (
unsigned int k = 0; k < nDriftLines; ++k) {
285 const auto& dl = driftLines[k];
287 if (statusCodes[k] != stat || ic >= dl.size())
continue;
289 std::array<double, 4> point = {dl[ic][0], dl[ic][1], dl[ic][2], 0.};
290 contour.push_back(std::make_pair(point, k));
293 if (contour.empty())
continue;
296 if (m_sortContours && !markers) SortContour(contour, circle);
300 std::vector<double> xp;
301 std::vector<double> yp;
302 std::vector<double> zp;
303 for (
const auto& point : contour) {
304 const double x = point.first[0];
305 const double y = point.first[1];
306 const double z = point.first[2];
311 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
321 std::vector<double> xp;
322 std::vector<double> yp;
323 std::vector<double> zp;
324 const unsigned int nP = contour.size();
325 for (
unsigned int i = 0; i < nP; ++i) {
327 const auto x0 = contour[i].first[0];
328 const auto y0 = contour[i].first[1];
329 const auto z0 = contour[i].first[2];
333 if (i == nP - 1)
break;
334 const auto x1 = contour[i + 1].first[0];
335 const auto y1 = contour[i + 1].first[1];
337 if (fabs(x1 - x0) > tolx || fabs(y1 - y0) > toly) gap =
true;
340 const auto i0 = contour[i].second;
341 const auto i1 = contour[i + 1].second;
343 if (m_checkCrossings && !gap) {
344 for (
unsigned int k = 0; k < nDriftLines; ++k) {
345 const auto& dl = driftLines[k];
346 for (
unsigned int jc = 0; jc < dl.size(); ++jc) {
347 if ((i0 == k || i1 == k) && (jc == ic || jc + 1 == ic)) {
350 const auto& p0 = dl[jc];
351 const auto& p1 = jc == dl.size() - 1 ? endPoints[k] : dl[jc + 1];
352 if (Crossing(p0[0], p0[1], p1[0], p1[1], x0, y0, x1, y1)) {
358 if ((i0 == k || i1 == k) && ic == 0)
continue;
359 const auto& p0 = startPoints[k];
360 if (Crossing(p0[0], p0[1], dl[0][0], dl[0][1],
371 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
374 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
383 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
384 }
else if (!xp.empty()) {
385 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Psame");
391 if (!plotDriftLines)
return;
393 graph.SetLineStyle(1);
395 graph.SetLineColor(kOrange - 3);
397 graph.SetLineColor(kRed + 1);
399 for (
unsigned int i = 0; i < nDriftLines; ++i) {
400 std::vector<double> xp;
401 std::vector<double> yp;
402 const double x0 = startPoints[i][0];
403 const double y0 = startPoints[i][1];
404 const double z0 = startPoints[i][2];
407 for (
const auto& point : driftLines[i]) {
408 const double x = point[0];
409 const double y = point[1];
410 const double z = point[2];
414 const double x1 = endPoints[i][0];
415 const double y1 = endPoints[i][1];
416 const double z1 = endPoints[i][2];
419 graph.DrawGraph(xp.size(), xp.data(), yp.data(),
"Lsame");
423void ViewIsochrons::ComputeDriftLines(
const double tstep,
424 const std::vector<std::array<double, 3> >& points,
425 std::vector<std::vector<std::array<double, 3> > >& driftLines,
426 std::vector<std::array<double, 3> >& startPoints,
427 std::vector<std::array<double, 3> >& endPoints,
428 std::vector<int>& statusCodes,
const bool rev) {
446 for (
const auto& point : points) {
455 drift.
DriftIon(point[0], point[1], point[2], 0.);
462 if (nu < 3)
continue;
464 double xf = 0., yf = 0., zf = 0., tf = 0.;
467 const unsigned int nSteps =
static_cast<unsigned int>(tf / tstep);
468 if (nSteps == 0)
continue;
469 std::vector<double> xu(nu, 0.);
470 std::vector<double> yu(nu, 0.);
471 std::vector<double> zu(nu, 0.);
472 std::vector<double> tu(nu, 0.);
473 for (
unsigned int i = 0; i < nu; ++i) {
477 for (
auto& t : tu) t = tf - t;
478 std::reverse(std::begin(xu), std::end(xu));
479 std::reverse(std::begin(yu), std::end(yu));
480 std::reverse(std::begin(zu), std::end(zu));
481 std::reverse(std::begin(tu), std::end(tu));
483 std::vector<std::array<double, 3> > tab;
485 for (
unsigned int i = 0; i < nSteps; ++i) {
486 const double t = (i + 1) * tstep;
490 std::array<double, 3> step = {Interpolate(xu, tu, t),
491 Interpolate(yu, tu, t),
492 Interpolate(zu, tu, t)};
495 driftLines.push_back(std::move(tab));
496 std::array<double, 3> start = {xu[0], yu[0], zu[0]};
497 std::array<double, 3> end = {xu[nu - 1], yu[nu - 1], zu[nu - 1]};
498 startPoints.push_back(std::move(start));
499 endPoints.push_back(std::move(end));
502 statusCodes.push_back(status);
504 statusCodes.push_back(0);
509bool ViewIsochrons::SetPlotLimits() {
512 double xmin = 0., ymin = 0., xmax = 0., ymax = 0.;
525 ok =
PlotLimits(m_sensor, xmin, ymin, xmax, ymax);
527 ok =
PlotLimits(m_component, xmin, ymin, xmax, ymax);
538void ViewIsochrons::SortContour(
539 std::vector<std::pair<std::array<double, 4>,
unsigned int> >& contour,
542 if (contour.size() < 2)
return;
546 for (
const auto& point : contour) {
547 xcog += point.first[0];
548 ycog += point.first[1];
550 const double scale = 1. / contour.size();
557 for (
const auto& point : contour) {
558 const double dx = point.first[0] - xcog;
559 const double dy = point.first[1] - ycog;
564 const double theta = 0.5 * atan2(2 * sxy, sxx - syy);
565 const double ct =
cos(theta);
566 const double st =
sin(theta);
570 for (
const auto& point : contour) {
571 const double dx = point.first[0] - xcog;
572 const double dy = point.first[1] - ycog;
573 sxx +=
fabs(+ct * dx + st * dy);
574 syy +=
fabs(-st * dx + ct * dy);
577 if (
fabs(sxx) > m_aspectRatio *
fabs(syy) ||
578 fabs(syy) > m_aspectRatio *
fabs(sxx)) {
584 for (
auto& point : contour) {
585 const double dx = point.first[0] - xcog;
586 const double dy = point.first[1] - ycog;
587 point.first[3] = circle ? atan2(dy, dx) : ct * dx +
st * dy;
590 std::sort(contour.begin(), contour.end(),
591 [](
const std::pair<std::array<double, 4>,
int>& p1,
592 const std::pair<std::array<double, 4>,
int>& p2) {
593 return p1.first[3] < p2.first[3]; }
600 unsigned int imax = 0;
601 const unsigned int nPoints = contour.size();
602 for (
unsigned int j = 0; j < nPoints; ++j) {
603 const auto& p1 = contour[j];
604 const auto& p0 = j > 0 ? contour[j - 1] : contour.back();
605 const double dx = p1.first[0] - p0.first[0];
606 const double dy = p1.first[1] - p0.first[1];
607 const double d =
sqrt(dx * dx + dy * dy);
608 if (j > 0) dsum += d;
614 if (dmax < m_loopThreshold * dsum) {
616 contour.push_back(contour[0]);
621 std::rotate(contour.begin(), contour.begin() + imax, contour.end());
Abstract base class for components.
void EnableSignalCalculation(const bool on=true)
Switch calculation of induced currents on or off (default: enabled).
bool DriftNegativeIon(const double x0, const double y0, const double z0, const double t0)
bool DriftIon(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an ion with a given starting point.
void SetSensor(Sensor *s)
Set the sensor.
void GetDriftLinePoint(const size_t i, double &x, double &y, double &z, double &t) const
Get the coordinates and time of a point along the most recent drift line.
bool DriftElectron(const double x0, const double y0, const double z0, const double t0)
Simulate the drift line of an electron with a given starting point.
bool DriftPositron(const double x0, const double y0, const double z0, const double t0)
void GetEndPoint(double &x, double &y, double &z, double &t, int &st) const
Get the end point and status flag of the most recent drift line.
size_t GetNumberOfDriftLinePoints() const
Get the number of points of the most recent drift line.
void SetMaximumStepSize(const double ms)
Set (explicitly) the maximum step size that is allowed.
void AddComponent(Component *comp)
Add a component.
bool SetArea(const bool verbose=false)
Set the user area to the default.
std::array< double, 4 > m_plane
std::array< std::array< double, 3 >, 3 > m_proj
TPad * GetCanvas()
Retrieve the canvas.
bool PlotLimits(Sensor *sensor, double &xmin, double &ymin, double &xmax, double &ymax) const
bool PlotLimitsFromUserBox(double &xmin, double &ymin, double &xmax, double &ymax) const
ViewBase()=delete
Default constructor.
void SetComponent(Component *c)
Set the component.
void PlotIsochrons(const double tstep, const std::vector< std::array< double, 3 > > &points, const bool rev=false, const bool colour=false, const bool markers=false, const bool plotDriftLines=true)
void SetConnectionThreshold(const double thr)
void SetAspectRatioSwitch(const double ar)
ViewIsochrons()
Constructor.
void SetLoopThreshold(const double thr)
void SetSensor(Sensor *s)
Set the sensor.
DoubleAc cos(const DoubleAc &f)
DoubleAc fabs(const DoubleAc &f)
DoubleAc sin(const DoubleAc &f)
DoubleAc sqrt(const DoubleAc &f)