Garfield++ 3.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Polygon.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <cmath>
3#include <iostream>
4#include <numeric>
5
6#include "Garfield/Polygon.hh"
9#include "Garfield/Random.hh"
10
11namespace {
12
13/// Determine whether a point (u, v) lies on a straight line
14/// (x1, y1) to (x2, y2).
15bool OnLine(const double x1, const double y1, const double x2, const double y2,
16 const double u, const double v) {
17 // Set tolerances.
18 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u)});
19 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v)});
20 epsx = std::max(1.e-10, epsx);
21 epsy = std::max(1.e-10, epsy);
22
23 if ((fabs(x1 - u) <= epsx && fabs(y1 - v) <= epsy) ||
24 (fabs(x2 - u) <= epsx && fabs(y2 - v) <= epsy)) {
25 // Point to be examined coincides with start or end.
26 return true;
27 } else if (fabs(x1 - x2) <= epsx && fabs(y1 - y2) <= epsy) {
28 // The line (x1, y1) to (x2, y2) is in fact a point.
29 return false;
30 }
31 double xc = 0., yc = 0.;
32 if (fabs(u - x1) + fabs(v - y1) < fabs(u - x2) + fabs(v - y2)) {
33 // (u, v) is nearer to (x1, y1).
34 const double dx = (x2 - x1);
35 const double dy = (y2 - y1);
36 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
37 if (xl < 0.) {
38 xc = x1;
39 yc = y1;
40 } else if (xl > 1.) {
41 xc = x2;
42 yc = y2;
43 } else {
44 xc = x1 + xl * dx;
45 yc = y1 + xl * dy;
46 }
47 } else {
48 // (u, v) is nearer to (x2, y2).
49 const double dx = (x1 - x2);
50 const double dy = (y1 - y2);
51 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
52 if (xl < 0.) {
53 xc = x2;
54 yc = y2;
55 } else if (xl > 1.) {
56 xc = x1;
57 yc = y1;
58 } else {
59 xc = x2 + xl * dx;
60 yc = y2 + xl * dy;
61 }
62 }
63 // See whether the point is on the line.
64 if (fabs(u - xc) < epsx && fabs(v - yc) < epsy) {
65 return true;
66 }
67 return false;
68}
69
70/// Determine whether the 2 straight lines (x1, y1) to (x2, y2)
71/// and (u1, v1) to (u2, v2) cross at an intermediate point for both lines.
72bool Crossing(const double x1, const double y1, const double x2,
73 const double y2, const double u1, const double v1,
74 const double u2, const double v2, double& xc, double& yc) {
75 /// Matrix to compute the crossing point.
76 std::array<std::array<double, 2>, 2> a;
77 a[0][0] = y2 - y1;
78 a[0][1] = v2 - v1;
79 a[1][0] = x1 - x2;
80 a[1][1] = u1 - u2;
81 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
82 // Initial values.
83 xc = 0.;
84 yc = 0.;
85 // Set tolerances.
86 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u1), fabs(u2)});
87 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v1), fabs(v2)});
88 epsx = std::max(epsx, 1.e-10);
89 epsy = std::max(epsy, 1.e-10);
90 // Check for a point of one line located on the other line.
91 if (OnLine(x1, y1, x2, y2, u1, v1)) {
92 xc = u1;
93 yc = v1;
94 return true;
95 } else if (OnLine(x1, y1, x2, y2, u2, v2)) {
96 xc = u2;
97 yc = v2;
98 return true;
99 } else if (OnLine(u1, v1, u2, v2, x1, y1)) {
100 xc = x1;
101 yc = y1;
102 return true;
103 } else if (OnLine(u1, v1, u2, v2, x2, y2)) {
104 xc = x2;
105 yc = y2;
106 return true;
107 } else if (fabs(det) < epsx * epsy) {
108 // Parallel, non-touching.
109 return false;
110 }
111 // Crossing, non-trivial lines: solve crossing equations.
112 const double aux = a[1][1];
113 a[1][1] = a[0][0] / det;
114 a[0][0] = aux / det;
115 a[1][0] = -a[1][0] / det;
116 a[0][1] = -a[0][1] / det;
117 // Compute crossing point.
118 xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
119 yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
120 // See whether the crossing point is on both lines.
121 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
122 // Intersecting lines.
123 return true;
124 }
125 // Crossing point not on both lines.
126 return false;
127}
128
129}
130
131namespace Garfield {
132
133namespace Polygon {
134
135void Inside(const std::vector<double>& xpl, const std::vector<double>& ypl,
136 const double x, const double y, bool& inside, bool& edge) {
137 // Initial settings.
138 inside = false;
139 edge = false;
140 const unsigned int npl = xpl.size();
141 if (ypl.size() != npl) return;
142 // Special treatment for few points.
143 if (npl < 2) {
144 return;
145 } else if (npl == 2) {
146 edge = OnLine(xpl[0], ypl[0], xpl[1], ypl[1], x, y);
147 return;
148 }
149 // Determine the range of the data.
150 const double xmin = *std::min_element(std::begin(xpl), std::end(xpl));
151 const double xmax = *std::max_element(std::begin(xpl), std::end(xpl));
152 const double ymin = *std::min_element(std::begin(ypl), std::end(ypl));
153 const double ymax = *std::max_element(std::begin(ypl), std::end(ypl));
154
155 // Set tolerances.
156 double epsx = 1.e-8 * std::max(fabs(xmin), fabs(xmax));
157 double epsy = 1.e-8 * std::max(fabs(ymin), fabs(ymax));
158 epsx = std::max(epsx, 1.e-8);
159 epsy = std::max(epsy, 1.e-8);
160
161 // Ensure that we have a range.
162 if (fabs(xmax - xmin) <= epsx) {
163 if (y >= ymin - epsy && y <= ymax + epsy &&
164 fabs(xmax + xmin - 2 * x) <= epsx) {
165 edge = true;
166 } else {
167 edge = false;
168 }
169 } else if (fabs(ymax - ymin) <= epsy) {
170 if (x >= xmin - epsx && x <= xmax + epsx &&
171 fabs(ymax + ymin - 2 * y) <= epsy) {
172 edge = true;
173 } else {
174 edge = false;
175 }
176 }
177 // Choose a point at "infinity".
178 double xinf = xmin - fabs(xmax - xmin);
179 double yinf = ymin - fabs(ymax - ymin);
180
181 unsigned int nIter = 0;
182 bool ok = false;
183 while (!ok && nIter < 100) {
184 ok = true;
185 // Loop over the edges counting intersections.
186 unsigned int nCross = 0;
187 for (unsigned int j = 0; j < npl; ++j) {
188 const unsigned int jj = j < npl - 1 ? j + 1 : 0;
189 // Flag points located on one of the edges.
190 if (OnLine(xpl[j], ypl[j], xpl[jj], ypl[jj], x, y)) {
191 edge = true;
192 return;
193 }
194 // Count mid-line intersects.
195 double xc = 0., yc = 0.;
196 if (Crossing(x, y, xinf, yinf, xpl[j], ypl[j], xpl[jj], ypl[jj], xc,
197 yc)) {
198 ++nCross;
199 }
200 // Ensure that the testing line doesn't cross a corner.
201 if (OnLine(x, y, xinf, yinf, xpl[j], ypl[j])) {
202 xinf = xmin - ::Garfield::RndmUniform() * fabs(xmax - xinf);
203 yinf = ymin - ::Garfield::RndmUniform() * fabs(ymax - yinf);
204 ok = false;
205 break;
206 }
207 }
208 if (ok) {
209 // Set the INSIDE flag.
210 if (nCross != 2 * (nCross / 2)) inside = true;
211 return;
212 }
213 ++nIter;
214 }
215
216 std::cerr << "Polygon::Inside:\n Warning. Unable to verify "
217 << "whether a point is internal; setting to edge.\n";
218 inside = false;
219 edge = true;
220}
221
222double Area(const std::vector<double>& xp, const std::vector<double>& yp) {
223
224 double f = 0.;
225 const unsigned int n = xp.size();
226 for (unsigned int i = 0; i < n; ++i) {
227 const unsigned int ii = i < n - 1 ? i + 1 : 0;
228 f += xp[i] * yp[ii] - xp[ii] * yp[i];
229 }
230 return 0.5 * f;
231}
232
233}
234
235}
double Area(const std::vector< double > &xp, const std::vector< double > &yp)
Determine the (signed) area of a polygon.
Definition: Polygon.cc:222
void Inside(const std::vector< double > &xpl, const std::vector< double > &ypl, const double x, const double y, bool &inside, bool &edge)
Definition: Polygon.cc:135
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
Definition: Random.hh:14
DoubleAc fabs(const DoubleAc &f)
Definition: DoubleAc.h:615