Garfield++ 4.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 <array>
2#include <algorithm>
3#include <cmath>
4#include <iostream>
5#include <numeric>
6
7#include "Garfield/Polygon.hh"
10#include "Garfield/Random.hh"
11
12namespace {
13
14/// Determine whether a point (u, v) lies on a straight line
15/// (x1, y1) to (x2, y2).
16bool OnLine(const double x1, const double y1, const double x2, const double y2,
17 const double u, const double v) {
18 // Set tolerances.
19 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u)});
20 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v)});
21 epsx = std::max(1.e-10, epsx);
22 epsy = std::max(1.e-10, epsy);
23
24 if ((fabs(x1 - u) <= epsx && fabs(y1 - v) <= epsy) ||
25 (fabs(x2 - u) <= epsx && fabs(y2 - v) <= epsy)) {
26 // Point to be examined coincides with start or end.
27 return true;
28 } else if (fabs(x1 - x2) <= epsx && fabs(y1 - y2) <= epsy) {
29 // The line (x1, y1) to (x2, y2) is in fact a point.
30 return false;
31 }
32 double xc = 0., yc = 0.;
33 if (fabs(u - x1) + fabs(v - y1) < fabs(u - x2) + fabs(v - y2)) {
34 // (u, v) is nearer to (x1, y1).
35 const double dx = (x2 - x1);
36 const double dy = (y2 - y1);
37 const double xl = ((u - x1) * dx + (v - y1) * dy) / (dx * dx + dy * dy);
38 if (xl < 0.) {
39 xc = x1;
40 yc = y1;
41 } else if (xl > 1.) {
42 xc = x2;
43 yc = y2;
44 } else {
45 xc = x1 + xl * dx;
46 yc = y1 + xl * dy;
47 }
48 } else {
49 // (u, v) is nearer to (x2, y2).
50 const double dx = (x1 - x2);
51 const double dy = (y1 - y2);
52 const double xl = ((u - x2) * dx + (v - y2) * dy) / (dx * dx + dy * dy);
53 if (xl < 0.) {
54 xc = x2;
55 yc = y2;
56 } else if (xl > 1.) {
57 xc = x1;
58 yc = y1;
59 } else {
60 xc = x2 + xl * dx;
61 yc = y2 + xl * dy;
62 }
63 }
64 // See whether the point is on the line.
65 if (fabs(u - xc) < epsx && fabs(v - yc) < epsy) {
66 return true;
67 }
68 return false;
69}
70
71/// Determine whether the 2 straight lines (x1, y1) to (x2, y2)
72/// and (u1, v1) to (u2, v2) cross at an intermediate point for both lines.
73bool Crossing(const double x1, const double y1, const double x2,
74 const double y2, const double u1, const double v1,
75 const double u2, const double v2, double& xc, double& yc) {
76 /// Matrix to compute the crossing point.
77 std::array<std::array<double, 2>, 2> a;
78 a[0][0] = y2 - y1;
79 a[0][1] = v2 - v1;
80 a[1][0] = x1 - x2;
81 a[1][1] = u1 - u2;
82 const double det = a[0][0] * a[1][1] - a[1][0] * a[0][1];
83 // Initial values.
84 xc = 0.;
85 yc = 0.;
86 // Set tolerances.
87 double epsx = 1.e-10 * std::max({fabs(x1), fabs(x2), fabs(u1), fabs(u2)});
88 double epsy = 1.e-10 * std::max({fabs(y1), fabs(y2), fabs(v1), fabs(v2)});
89 epsx = std::max(epsx, 1.e-10);
90 epsy = std::max(epsy, 1.e-10);
91 // Check for a point of one line located on the other line.
92 if (OnLine(x1, y1, x2, y2, u1, v1)) {
93 xc = u1;
94 yc = v1;
95 return true;
96 } else if (OnLine(x1, y1, x2, y2, u2, v2)) {
97 xc = u2;
98 yc = v2;
99 return true;
100 } else if (OnLine(u1, v1, u2, v2, x1, y1)) {
101 xc = x1;
102 yc = y1;
103 return true;
104 } else if (OnLine(u1, v1, u2, v2, x2, y2)) {
105 xc = x2;
106 yc = y2;
107 return true;
108 } else if (fabs(det) < epsx * epsy) {
109 // Parallel, non-touching.
110 return false;
111 }
112 // Crossing, non-trivial lines: solve crossing equations.
113 const double aux = a[1][1];
114 a[1][1] = a[0][0] / det;
115 a[0][0] = aux / det;
116 a[1][0] = -a[1][0] / det;
117 a[0][1] = -a[0][1] / det;
118 // Compute crossing point.
119 xc = a[0][0] * (x1 * y2 - x2 * y1) + a[1][0] * (u1 * v2 - u2 * v1);
120 yc = a[0][1] * (x1 * y2 - x2 * y1) + a[1][1] * (u1 * v2 - u2 * v1);
121 // See whether the crossing point is on both lines.
122 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
123 // Intersecting lines.
124 return true;
125 }
126 // Crossing point not on both lines.
127 return false;
128}
129
130/// Determines whether the 2 straight lines (x1, y1) to (x2, y2)
131/// and (u1, u2) to (v1, v2) cross at an intermediate point for both lines.
132bool Crossing(const double x1, const double y1,
133 const double x2, const double y2,
134 const double u1, const double v1,
135 const double u2, const double v2) {
136
137 std::array<std::array<double, 2>, 2> a;
138 // Matrix to compute the crossing point.
139 a[0][0] = y2 - y1;
140 a[1][0] = v2 - v1;
141 a[0][1] = x1 - x2;
142 a[1][1] = u1 - u2;
143 const double det = a[0][0] * a[1][1] - a[0][1] * a[1][0];
144 // Set tolerances.
145 const double epsx = std::max(1.e-10 * std::max({std::abs(x1), std::abs(x2),
146 std::abs(u1), std::abs(u2)}),
147 1.e-10);
148 const double epsy = std::max(1.e-10 * std::max({std::abs(y1), std::abs(y2),
149 std::abs(v1), std::abs(v2)}),
150 1.e-10);
151 // Check for a point of one line located on the other line.
152 if (OnLine(x1, y1, x2, y2, u1, v1) || OnLine(x1, y1, x2, y2, u2, v2) ||
153 OnLine(u1, v1, u2, v2, x1, y1) || OnLine(u1, v1, u2, v2, x2, y2)) {
154 // Point on other line.
155 return true;
156 }
157 if (std::abs(det) < epsx * epsy) {
158 // Parallel, non-touching
159 return false;
160 }
161 // Crossing, non-trivial lines: solve crossing equations.
162 std::swap(a[0][0], a[1][1]);
163 const double invdet = 1. / det;
164 a[0][0] *= invdet;
165 a[1][1] *= invdet;
166 a[0][1] = -a[0][1] * invdet;
167 a[1][0] = -a[1][0] * invdet;
168 // Compute crossing point.
169 const double xc = a[0][0] * (x1 * y2 - x2 * y1) +
170 a[0][1] * (u1 * v2 - u2 * v1);
171 const double yc = a[1][0] * (x1 * y2 - x2 * y1) +
172 a[1][1] * (u1 * v2 - u2 * v1);
173 // See whether the crossing point is on both lines.
174 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc)) {
175 return true;
176 }
177 // Crossing point not on both lines.
178 return false;
179}
180
181}
182
183namespace Garfield {
184
185namespace Polygon {
186
187void Inside(const std::vector<double>& xpl, const std::vector<double>& ypl,
188 const double x, const double y, bool& inside, bool& edge) {
189 // Initial settings.
190 inside = false;
191 edge = false;
192 const unsigned int npl = xpl.size();
193 if (ypl.size() != npl) return;
194 // Special treatment for few points.
195 if (npl < 2) {
196 return;
197 } else if (npl == 2) {
198 edge = OnLine(xpl[0], ypl[0], xpl[1], ypl[1], x, y);
199 return;
200 }
201 // Determine the range of the data.
202 const double xmin = *std::min_element(std::begin(xpl), std::end(xpl));
203 const double xmax = *std::max_element(std::begin(xpl), std::end(xpl));
204 const double ymin = *std::min_element(std::begin(ypl), std::end(ypl));
205 const double ymax = *std::max_element(std::begin(ypl), std::end(ypl));
206
207 // Set tolerances.
208 double epsx = 1.e-8 * std::max(fabs(xmin), fabs(xmax));
209 double epsy = 1.e-8 * std::max(fabs(ymin), fabs(ymax));
210 epsx = std::max(epsx, 1.e-8);
211 epsy = std::max(epsy, 1.e-8);
212
213 // Ensure that we have a range.
214 if (fabs(xmax - xmin) <= epsx) {
215 if (y >= ymin - epsy && y <= ymax + epsy &&
216 fabs(xmax + xmin - 2 * x) <= epsx) {
217 edge = true;
218 } else {
219 edge = false;
220 }
221 } else if (fabs(ymax - ymin) <= epsy) {
222 if (x >= xmin - epsx && x <= xmax + epsx &&
223 fabs(ymax + ymin - 2 * y) <= epsy) {
224 edge = true;
225 } else {
226 edge = false;
227 }
228 }
229 // Choose a point at "infinity".
230 double xinf = xmin - fabs(xmax - xmin);
231 double yinf = ymin - fabs(ymax - ymin);
232
233 unsigned int nIter = 0;
234 bool ok = false;
235 while (!ok && nIter < 100) {
236 ok = true;
237 // Loop over the edges counting intersections.
238 unsigned int nCross = 0;
239 for (unsigned int j = 0; j < npl; ++j) {
240 const unsigned int jj = j < npl - 1 ? j + 1 : 0;
241 // Flag points located on one of the edges.
242 if (OnLine(xpl[j], ypl[j], xpl[jj], ypl[jj], x, y)) {
243 edge = true;
244 return;
245 }
246 // Count mid-line intersects.
247 double xc = 0., yc = 0.;
248 if (Crossing(x, y, xinf, yinf, xpl[j], ypl[j], xpl[jj], ypl[jj], xc,
249 yc)) {
250 ++nCross;
251 }
252 // Ensure that the testing line doesn't cross a corner.
253 if (OnLine(x, y, xinf, yinf, xpl[j], ypl[j])) {
254 xinf = xmin - ::Garfield::RndmUniform() * fabs(xmax - xinf);
255 yinf = ymin - ::Garfield::RndmUniform() * fabs(ymax - yinf);
256 ok = false;
257 break;
258 }
259 }
260 if (ok) {
261 // Set the INSIDE flag.
262 if (nCross != 2 * (nCross / 2)) inside = true;
263 return;
264 }
265 ++nIter;
266 }
267
268 std::cerr << "Polygon::Inside:\n Warning. Unable to verify "
269 << "whether a point is internal; setting to edge.\n";
270 inside = false;
271 edge = true;
272}
273
274double Area(const std::vector<double>& xp, const std::vector<double>& yp) {
275
276 double f = 0.;
277 const unsigned int n = xp.size();
278 for (unsigned int i = 0; i < n; ++i) {
279 const unsigned int ii = i < n - 1 ? i + 1 : 0;
280 f += xp[i] * yp[ii] - xp[ii] * yp[i];
281 }
282 return 0.5 * f;
283}
284
285bool NonTrivial(const std::vector<double>& xp,
286 const std::vector<double>& yp) {
287
288 // -----------------------------------------------------------------------
289 // PLACHK - Checks whether a set of points builds a non-trivial
290 // polygon in the (x,y) plane.
291 // -----------------------------------------------------------------------
292
293 // First check number of points.
294 if (xp.size() != yp.size()) return false;
295 const unsigned int np = xp.size();
296 if (xp.size() < 3) return false;
297 // Find a second point at maximum distance of the first.
298 double d1 = 0.;
299 double x1 = 0.;
300 double y1 = 0.;
301 unsigned int i1 = 0;
302 double xmin = xp[0];
303 double ymin = yp[0];
304 double xmax = xp[0];
305 double ymax = yp[0];
306 for (unsigned int i = 1; i < np; ++i) {
307 xmin = std::min(xmin, xp[i]);
308 ymin = std::min(ymin, yp[i]);
309 xmax = std::max(xmax, xp[i]);
310 ymax = std::max(ymax, yp[i]);
311 const double dx = xp[i] - xp[0];
312 const double dy = yp[i] - yp[0];
313 const double d = dx * dx + dy * dy;
314 if (d > d1) {
315 x1 = dx;
316 y1 = dy;
317 i1 = i;
318 d1 = d;
319 }
320 }
321 // Set tolerances.
322 double epsx = 1.e-6 * (std::abs(xmax) + std::abs(xmin));
323 double epsy = 1.e-6 * (std::abs(ymax) + std::abs(ymin));
324 if (epsx <= 0) epsx = 1.e-6;
325 if (epsy <= 0) epsy = 1.e-6;
326 // See whether there is a range at all.
327 if (std::abs(xmax - xmin) <= epsx && std::abs(ymax - ymin) <= epsy) {
328 // Single point.
329 return false;
330 }
331 // See whether there is a second point.
332 if (d1 <= epsx * epsx + epsy * epsy || i1 == 0) return false;
333
334 // Find a third point maximising the external product.
335 double d2 = 0.;
336 unsigned int i2 = 0;
337 for (unsigned int i = 1; i < np; ++i) {
338 if (i == i1) continue;
339 const double dx = xp[i] - xp[0];
340 const double dy = yp[i] - yp[0];
341 const double d = std::abs(x1 * dy - y1 * dx);
342 if (d > d2) {
343 d2 = d;
344 i2 = i;
345 }
346 }
347 if (d2 <= epsx * epsy || i2 <= 0) {
348 // No third point.
349 return false;
350 }
351 // Seems to be OK.
352 return true;
353}
354
355void EliminateButterflies(std::vector<double>& xp, std::vector<double>& yp,
356 std::vector<double>& zp) {
357 //----------------------------------------------------------------------
358 // BUTFLD - Tries to eliminate "butterflies", i.e. the crossing of 2
359 // adjacent segments of a polygon, by point exchanges.
360 //----------------------------------------------------------------------
361 unsigned int np = xp.size();
362 if (np <= 3) return;
363 // Compute range.
364 const double xmin = *std::min_element(std::begin(xp), std::end(xp));
365 const double xmax = *std::max_element(std::begin(xp), std::end(xp));
366 const double ymin = *std::min_element(std::begin(yp), std::end(yp));
367 const double ymax = *std::max_element(std::begin(yp), std::end(yp));
368 const double zmin = *std::min_element(std::begin(zp), std::end(zp));
369 const double zmax = *std::max_element(std::begin(zp), std::end(zp));
370 // Set tolerances.
371 const double epsx = std::max(1.e-10 * std::abs(xmax - xmin), 1.e-6);
372 const double epsy = std::max(1.e-10 * std::abs(ymax - ymin), 1.e-6);
373 const double epsz = std::max(1.e-10 * std::abs(zmax - zmin), 1.e-6);
374 double xsurf = 0.;
375 double ysurf = 0.;
376 double zsurf = 0.;
377 for (unsigned int i = 2; i < np; ++i) {
378 const double x1 = xp[i - 1] - xp[0];
379 const double y1 = yp[i - 1] - yp[0];
380 const double z1 = zp[i - 1] - zp[0];
381 xsurf += std::abs((yp[i] - yp[0]) * z1 - y1 * (zp[i] - zp[0]));
382 ysurf += std::abs((xp[i] - xp[0]) * z1 - x1 * (zp[i] - zp[0]));
383 zsurf += std::abs((xp[i] - xp[0]) * y1 - x1 * (yp[i] - yp[0]));
384 }
385 // Eliminate points appearing twice, initialise marks.
386 std::vector<bool> mark(np, false);
387 // Scan the list.
388 for (unsigned int i = 0; i < np; ++i) {
389 if (mark[i]) continue;
390 for (unsigned int j = i + 1; j < np; ++j) {
391 if (std::abs(xp[i] - xp[j]) <= epsx && std::abs(yp[i] - yp[j]) <= epsy &&
392 std::abs(zp[i] - zp[j]) <= epsz) {
393 mark[j] = true;
394 }
395 }
396 }
397 // And remove the duplicate points.
398 unsigned int nNew = 0;
399 for (unsigned int i = 0; i < np; ++i) {
400 if (mark[i]) continue;
401 xp[nNew] = xp[i];
402 yp[nNew] = yp[i];
403 zp[nNew] = zp[i];
404 ++nNew;
405 }
406 // std::cout << "ElminateButterflies: old/new number of points: " << np
407 // << "/" << nNew << "\n";
408 // Update the number of points.
409 np = nNew;
410 xp.resize(np);
411 yp.resize(np);
412 zp.resize(np);
413 // No risk of having a butterfly with less than 4 points.
414 if (np <= 3) return;
415 // Select the axis with the largest norm.
416 unsigned int iaxis = 0;
417 if (xsurf > ysurf && xsurf > zsurf) {
418 iaxis = 1;
419 } else if (ysurf > zsurf) {
420 iaxis = 2;
421 } else {
422 iaxis = 3;
423 }
424 // Set number of passes to avoid endless loop.
425 unsigned int nPass = 0;
426 bool repass = true;
427 while (repass) {
428 // Make a pass.
429 ++nPass;
430 repass = false;
431 for (unsigned int i = 0; i < np; ++i) {
432 const unsigned int ii = (i + 1) % np;
433 for (unsigned int j = i + 2; j < np; ++j) {
434 const unsigned int jj = (j + 1) % np;
435 if (j + 1 >= np && jj >= i) continue;
436 // Check for a crossing.
437 if (iaxis == 1 && !Crossing(yp[i], zp[i], yp[ii], zp[ii],
438 yp[j], zp[j], yp[jj], zp[jj])) {
439 continue;
440 } else if (iaxis == 2 && !Crossing(xp[i], zp[i], xp[ii], zp[ii],
441 xp[j], zp[j], xp[jj], zp[jj])) {
442 continue;
443 } else if (iaxis == 3 && !Crossing(xp[i], yp[i], xp[ii], yp[ii],
444 xp[j], yp[j], xp[jj], yp[jj])) {
445 continue;
446 }
447 // If there is a crossing, exchange the portion in between.
448 for (unsigned int k = 0; k < (j - i) / 2; ++k) {
449 const unsigned int k1 = (i + k + 1) % np;
450 const unsigned int k2 = (j - k) % np;
451 std::swap(xp[k1], xp[k2]);
452 std::swap(yp[k1], yp[k2]);
453 std::swap(zp[k1], zp[k2]);
454 }
455 // And remember we have to do another pass after this.
456 repass = true;
457 }
458 }
459 // See whether we have to do another pass.
460 if (repass && nPass > np) {
461 std::cerr << "Butterfly: unable to eliminate the internal crossings;\n"
462 << " plot probably incorrect.\n";
463 break;
464 }
465 }
466}
467
468}
469
470}
bool NonTrivial(const std::vector< double > &xp, const std::vector< double > &yp)
Check whether a set of points builds a non-trivial polygon.
Definition: Polygon.cc:285
double Area(const std::vector< double > &xp, const std::vector< double > &yp)
Determine the (signed) area of a polygon.
Definition: Polygon.cc:274
void EliminateButterflies(std::vector< double > &xp, std::vector< double > &yp, std::vector< double > &zp)
Definition: Polygon.cc:355
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:187
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