Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
ViewFEMesh.cc
Go to the documentation of this file.
1#include <algorithm>
2#include <iomanip>
3#include <iostream>
4#include <sstream>
5
6#include <TH1F.h>
7#include <TPolyLine.h>
8#include <TGraph.h>
9#include <TGeoSphere.h>
10#include <TPolyLine3D.h>
11
15#include "Garfield/Plotting.hh"
16#include "Garfield/Random.hh"
17#include "Garfield/TGeoTet.hh"
19
20namespace Garfield {
21
22ViewFEMesh::ViewFEMesh() : ViewBase("ViewFEMesh") {}
23
25 Reset();
26}
27
28void ViewFEMesh::Reset() {
29 for (auto it = m_volumes.begin(), end = m_volumes.end(); it != end; ++it) {
30 if (*it) {
31 TGeoShape* shape = (*it)->GetShape();
32 if (shape) delete shape;
33 delete *it;
34 }
35 }
36 m_volumes.clear();
37 for (auto it = m_media.begin(), end = m_media.end(); it != end; ++it) {
38 if (*it) {
39 TGeoMaterial* material = (*it)->GetMaterial();
40 if (material) delete material;
41 delete *it;
42 }
43 }
44 m_media.clear();
45
46 m_geoManager.reset(nullptr);
47}
48
50 if (!cmp) {
51 std::cerr << m_className << "::SetComponent: Null pointer.\n";
52 return;
53 }
54
55 m_cmp = cmp;
56}
57
58// The plotting functionality here is ported from Garfield
59// with some inclusion of code from ViewCell.cc
60bool ViewFEMesh::Plot(const bool twod) {
61 if (!m_cmp) {
62 std::cerr << m_className << "::Plot: Component is not defined.\n";
63 return false;
64 }
65
66 double pmin = 0., pmax = 0.;
67 if (!m_cmp->GetVoltageRange(pmin, pmax)) {
68 std::cerr << m_className << "::Plot: Component is not ready.\n";
69 return false;
70 }
71
72 auto pad = GetCanvas();
73 pad->cd();
74
75 if (!twod) {
76 if (!m_cmp->m_is3d) {
77 std::cerr << m_className << "::Plot:\n"
78 << " Cannot plot 2D mesh elements in 3D.\n";
79 return false;
80 }
81 DrawElements3d();
82 DrawDriftLines3d();
83 return true;
84 }
85
86 if (!GetPlotLimits()) return false;
87
88 if (!RangeSet(pad)) {
90 }
91
92 if (m_drawAxes) {
93 if (!m_xaxis && !m_yaxis) {
94 // Draw default axes.
95 auto frame = pad->DrawFrame(m_xMinPlot, m_yMinPlot,
97 if (m_xaxisTitle.empty()) {
98 frame->GetXaxis()->SetTitle(LabelX().c_str());
99 } else {
100 frame->GetXaxis()->SetTitle(m_xaxisTitle.c_str());
101 }
102 if (m_yaxisTitle.empty()) {
103 frame->GetYaxis()->SetTitle(LabelY().c_str());
104 } else {
105 frame->GetYaxis()->SetTitle(m_yaxisTitle.c_str());
106 }
107 } else {
108 // Draw custom axes.
109 if (m_xaxis) m_xaxis->Draw();
110 if (m_yaxis) m_yaxis->Draw();
111 }
112 }
113
114 // Plot the mesh elements.
115 auto cst = dynamic_cast<ComponentCST*>(m_cmp);
116 if (cst) {
117 std::cout << m_className << "::Plot: CST component. Calling DrawCST.\n";
118 DrawCST(cst);
119 } else {
120 DrawElements2d();
121 }
122
123 DrawDriftLines2d();
124
125 if (m_drawViewRegion && !m_viewRegionX.empty()) {
126 TPolyLine poly;
127 poly.SetLineColor(kSpring + 4);
128 poly.SetLineWidth(3);
129 std::vector<double> xv = m_viewRegionX;
130 std::vector<double> yv = m_viewRegionY;
131 // Close the polygon.
132 xv.push_back(m_viewRegionX[0]);
133 yv.push_back(m_viewRegionY[0]);
134 poly.DrawPolyLine(xv.size(), xv.data(), yv.data(), "same");
135 }
136 gPad->Update();
137 // Draw axes again so they are on top
138 gPad->RedrawAxis("g");
139 return true;
140}
141
142bool ViewFEMesh::GetPlotLimits() {
143
144 if (m_userPlotLimits) {
145 std::vector<double> xp = {m_xMinPlot, m_xMinPlot, m_xMaxPlot, m_xMaxPlot};
146 std::vector<double> yp = {m_yMinPlot, m_yMaxPlot, m_yMaxPlot, m_yMinPlot};
147 std::vector<double> xg(4, 0.);
148 std::vector<double> yg(4, 0.);
149 std::vector<double> zg(4, 0.);
150 for (size_t i = 0; i < 4; ++i) {
151 xg[i] = m_proj[0][0] * xp[i] + m_proj[1][0] * yp[i] + m_proj[2][0];
152 yg[i] = m_proj[0][1] * xp[i] + m_proj[1][1] * yp[i] + m_proj[2][1];
153 zg[i] = m_proj[0][2] * xp[i] + m_proj[1][2] * yp[i] + m_proj[2][2];
154 }
155 m_xMinBox = *std::min_element(xg.begin(), xg.end());
156 m_xMaxBox = *std::max_element(xg.begin(), xg.end());
157 m_yMinBox = *std::min_element(yg.begin(), yg.end());
158 m_yMaxBox = *std::max_element(yg.begin(), yg.end());
159 m_zMinBox = *std::min_element(zg.begin(), zg.end());
160 m_zMaxBox = *std::max_element(zg.begin(), zg.end());
161 m_viewRegionX = xp;
162 m_viewRegionY = yp;
163 return true;
164 }
165 if (!m_userBox) {
166 // If not set by the user, get the bounding box of the component.
167 if (!m_cmp) return false;
168 if (!m_cmp->GetBoundingBox(m_xMinBox, m_yMinBox, m_zMinBox,
170 std::cerr << m_className << "::GetPlotLimits:\n"
171 << " Bounding box of the component is not defined.\n"
172 << " Please set the limits explicitly (SetArea).\n";
173 return false;
174 }
175 if (std::isinf(m_xMinBox) || std::isinf(m_xMaxBox) ||
176 std::isinf(m_yMinBox) || std::isinf(m_yMaxBox) ||
177 std::isinf(m_zMinBox) || std::isinf(m_zMaxBox)) {
178 double x0 = 0., y0 = 0., z0 = 0.;
179 double x1 = 0., y1 = 0., z1 = 0.;
180 if (!m_cmp->GetElementaryCell(x0, y0, z0, x1, y1, z1)) {
181 std::cerr << m_className << "::GetPlotLimits:\n"
182 << " Cell boundaries are not defined.\n"
183 << " Please set the limits explicitly (SetArea).\n";
184 }
185 if (std::isinf(m_xMinBox) || std::isinf(m_xMaxBox)) {
186 m_xMinBox = x0;
187 m_xMaxBox = x1;
188 }
189 if (std::isinf(m_yMinBox) || std::isinf(m_yMaxBox)) {
190 m_yMinBox = y0;
191 m_yMaxBox = y1;
192 }
193 if (std::isinf(m_zMinBox) || std::isinf(m_zMaxBox)) {
194 m_zMinBox = z0;
195 m_zMaxBox = z1;
196 }
197 }
198 }
199 // Determine the intersection of the viewing plane and the box.
200 double xmin = 0., xmax = 0., ymin = 0., ymax = 0.;
201 IntersectPlaneArea(xmin, ymin, xmax, ymax);
202 if (m_viewRegionX.empty()) {
203 std::cerr << m_className << "::GetPlotLimits: Empty view.\n"
204 << " Make sure the viewing plane (SetPlane)\n"
205 << " intersects with the bounding box.\n";
206 return false;
207 }
208 m_xMinPlot = xmin;
209 m_xMaxPlot = xmax;
210 m_yMinPlot = ymin;
211 m_yMaxPlot = ymax;
212 return true;
213}
214
215void ViewFEMesh::SetPlane(const double fx, const double fy, const double fz,
216 const double x0, const double y0, const double z0) {
217 if (fy * fy + fz * fz > 0) {
218 SetPlane(fx, fy, fz, x0, y0, z0, 1, 0, 0);
219 } else {
220 SetPlane(fx, fy, fz, x0, y0, z0, 0, 1, 0);
221 }
222}
223
224void ViewFEMesh::SetPlane(const double fx, const double fy, const double fz,
225 const double x0, const double y0, const double z0,
226 const double hx, const double hy, const double hz) {
227 ViewBase::SetPlane(fx, fy, fz, x0, y0, z0, hx, hy, hz);
228}
229
230// Set the x-axis.
231void ViewFEMesh::SetXaxis(TGaxis* ax) { m_xaxis = ax; }
232
233// Set the y-axis.
234void ViewFEMesh::SetYaxis(TGaxis* ay) { m_yaxis = ay; }
235
236// Create default axes
238 // Create a new x and y axis.
239 if (!GetPlotLimits()) {
240 std::cerr << m_className << "::CreateDefaultAxes:\n"
241 << " Cannot determine the axis limits.\n";
242 return;
243 }
244 const double dx = std::abs(m_xMaxPlot - m_xMinPlot) * 0.1;
245 const double dy = std::abs(m_yMaxPlot - m_yMinPlot) * 0.1;
246 const double x0 = m_xMinPlot + dx;
247 const double y0 = m_yMinPlot + dy;
248 const double x1 = m_xMaxPlot - dx;
249 const double y1 = m_yMaxPlot - dy;
250 m_xaxis = new TGaxis(x0, y0, x1, y0, x0, x1, 2405, "x");
251 m_yaxis = new TGaxis(x0, y0, x0, y1, y0, y1, 2405, "y");
252
253 // Label sizes
254 m_xaxis->SetLabelSize(0.025);
255 m_yaxis->SetLabelSize(0.025);
256
257 // Titles
258 m_xaxis->SetTitleSize(0.03);
259 m_xaxis->SetTitle(LabelX().c_str());
260 m_yaxis->SetTitleSize(0.03);
261 m_yaxis->SetTitle(LabelY().c_str());
262}
263
264// Use ROOT plotting functions to draw the mesh elements on the canvas.
265// General methodology ported from Garfield
266void ViewFEMesh::DrawElements2d() {
267 // Get the map boundaries from the component.
268 double mapxmax = m_cmp->m_mapmax[0];
269 double mapxmin = m_cmp->m_mapmin[0];
270 double mapymax = m_cmp->m_mapmax[1];
271 double mapymin = m_cmp->m_mapmin[1];
272 double mapzmax = m_cmp->m_mapmax[2];
273 double mapzmin = m_cmp->m_mapmin[2];
274
275 // Get the periodicities.
276 double sx = mapxmax - mapxmin;
277 double sy = mapymax - mapymin;
278 double sz = mapzmax - mapzmin;
279 const bool perX = m_cmp->m_periodic[0] || m_cmp->m_mirrorPeriodic[0];
280 const bool perY = m_cmp->m_periodic[1] || m_cmp->m_mirrorPeriodic[1];
281 const bool perZ = m_cmp->m_periodic[2] || m_cmp->m_mirrorPeriodic[2];
282
283 // Get the plane information.
284 const double fx = m_plane[0];
285 const double fy = m_plane[1];
286 const double fz = m_plane[2];
287 const double dist = m_plane[3];
288
289 // Construct single-column matrix for use as coordinate vector.
290 TMatrixD xMat(3, 1);
291
292 // Determine the number of periods present in the cell.
293 const int nMinX = perX ? int(m_xMinBox / sx) - 1 : 0;
294 const int nMaxX = perX ? int(m_xMaxBox / sx) + 1 : 0;
295 const int nMinY = perY ? int(m_yMinBox / sy) - 1 : 0;
296 const int nMaxY = perY ? int(m_yMaxBox / sy) + 1 : 0;
297 const int nMinZ = perZ ? int(m_zMinBox / sz) - 1 : 0;
298 const int nMaxZ = perZ ? int(m_zMaxBox / sz) + 1 : 0;
299
300 bool cst = false;
301 if (dynamic_cast<ComponentCST*>(m_cmp)) cst = true;
302
303 // Loop over all elements.
304 const auto nElements = m_cmp->GetNumberOfElements();
305 for (size_t i = 0; i < nElements; ++i) {
306 size_t mat = 0;
307 bool driftmedium = false;
308 std::vector<size_t> nodes;
309 if (!m_cmp->GetElement(i, mat, driftmedium, nodes)) continue;
310 // Do not plot the drift medium.
311 if (driftmedium && !m_plotMeshBorders) continue;
312 // Do not create polygons for disabled materials.
313 if (m_disabledMaterial.count(mat) > 0 && m_disabledMaterial[mat]) {
314 continue;
315 }
316 TGraph gr;
317 const short col = m_colorMap.count(mat) != 0 ? m_colorMap[mat] : 1;
318 gr.SetLineColor(col);
319 if (m_colorMap_fill.count(mat) != 0) {
320 gr.SetFillColor(m_colorMap_fill[mat]);
321 } else {
322 gr.SetFillColor(col);
323 }
324 gr.SetLineWidth(3);
325 std::string opt = "";
326 if (m_plotMeshBorders || !m_fillMesh) opt += "l";
327 if (m_fillMesh) opt += "f";
328 opt += "same";
329 // Get the vertex coordinates in the basic cell.
330 std::vector<double> vx0;
331 std::vector<double> vy0;
332 std::vector<double> vz0;
333 const size_t nNodes = nodes.size();
334 for (size_t j = 0; j < nNodes; ++j) {
335 double xn = 0., yn = 0., zn = 0.;
336 if (!m_cmp->GetNode(nodes[j], xn, yn, zn)) continue;
337 vx0.push_back(xn);
338 vy0.push_back(yn);
339 vz0.push_back(zn);
340 }
341 if (vx0.size() != nNodes) {
342 std::cerr << m_className << "::DrawElements2d:\n"
343 << " Error retrieving nodes of element " << i << ".\n";
344 continue;
345 }
346 // Coordinates of vertices
347 std::vector<double> vx(nNodes, 0.);
348 std::vector<double> vy(nNodes, 0.);
349 std::vector<double> vz(nNodes, 0.);
350 // Loop over the periodicities in x.
351 for (int nx = nMinX; nx <= nMaxX; nx++) {
352 const double dx = sx * nx;
353 // Determine the x-coordinates of the vertices.
354 if (m_cmp->m_mirrorPeriodic[0] && nx != 2 * (nx / 2)) {
355 for (size_t j = 0; j < nNodes; ++j) {
356 vx[j] = mapxmin + (mapxmax - vx0[j]) + dx;
357 }
358 } else {
359 for (size_t j = 0; j < nNodes; ++j) {
360 vx[j] = vx0[j] + dx;
361 }
362 }
363
364 // Loop over the periodicities in y.
365 for (int ny = nMinY; ny <= nMaxY; ny++) {
366 const double dy = sy * ny;
367 // Determine the y-coordinates of the vertices.
368 if (m_cmp->m_mirrorPeriodic[1] && ny != 2 * (ny / 2)) {
369 for (size_t j = 0; j < nNodes; ++j) {
370 vy[j] = mapymin + (mapymax - vy0[j]) + dy;
371 }
372 } else {
373 for (size_t j = 0; j < nNodes; ++j) {
374 vy[j] = vy0[j] + dy;
375 }
376 }
377
378 // Loop over the periodicities in z.
379 for (int nz = nMinZ; nz <= nMaxZ; nz++) {
380 const double dz = sz * nz;
381 // Determine the z-coordinates of the vertices.
382 if (m_cmp->m_mirrorPeriodic[2] && nz != 2 * (nz / 2)) {
383 for (size_t j = 0; j < nNodes; ++j) {
384 vz[j] = mapzmin + (mapzmax - vz0[j]) + dz;
385 }
386 } else {
387 for (size_t j = 0; j < nNodes; ++j) {
388 vz[j] = vz0[j] + dz;
389 }
390 }
391
392 // Store the x and y coordinates of the relevant mesh vertices.
393 std::vector<double> vX;
394 std::vector<double> vY;
395
396 // Value used to determine whether a vertex is in the plane.
397 const double pcf = std::max(
398 {std::abs(vx[0]), std::abs(vy[0]), std::abs(vz[0]),
399 std::abs(fx), std::abs(fy), std::abs(fz), std::abs(dist)});
400 const double tol = 1.e-4 * pcf;
401 // First isolate the vertices that are in the viewing plane.
402 std::vector<bool> in(nNodes, false);
403 int cnt = 0;
404 for (size_t j = 0; j < nNodes; ++j) {
405 const double d = fx * vx[j] + fy * vy[j] + fz * vz[j] - dist;
406 if (std::abs(d) < tol) {
407 // Point is in the plane.
408 in[j] = true;
409 // Calculate the planar coordinates.
410 double xp = 0., yp = 0.;
411 ToPlane(vx[j], vy[j], vz[j], xp, yp);
412 vX.push_back(xp);
413 vY.push_back(yp);
414 } else {
415 if (d > 0.) {
416 cnt += 1;
417 } else {
418 cnt -= 1;
419 }
420 }
421 }
422 // Stop if all points are on the same side of the plane.
423 if (std::abs(cnt) == (int)nNodes) continue;
424 // Cut the sides that are not in the plane.
425 if (cst) {
426 const std::array<std::array<unsigned int, 3>, 8> neighbours = {{
427 {1, 2, 4}, {0, 3, 5}, {0, 3, 6}, {1, 2, 7},
428 {0, 5, 6}, {1, 4, 7}, {2, 4, 7}, {3, 5, 6}
429 }};
430 for (size_t j = 0; j < nNodes; ++j) {
431 for (unsigned int k : neighbours[j]) {
432 if (in[j] || in[k]) continue;
433 if (PlaneCut(vx[j], vy[j], vz[j],
434 vx[k], vy[k], vz[k], xMat)) {
435 vX.push_back(xMat(0, 0));
436 vY.push_back(xMat(1, 0));
437 }
438 }
439 }
440 } else {
441 // Tetrahedron.
442 for (size_t j = 0; j < nNodes; ++j) {
443 for (size_t k = j + 1; k < nNodes; ++k) {
444 if (in[j] || in[k]) continue;
445 if (PlaneCut(vx[j], vy[j], vz[j],
446 vx[k], vy[k], vz[k], xMat)) {
447 vX.push_back(xMat(0, 0));
448 vY.push_back(xMat(1, 0));
449 }
450 }
451 }
452 }
453 if (vX.size() < 3) continue;
454
455 // Eliminate crossings of the polygon lines
456 // (known as "butterflies" in Garfield).
457 RemoveCrossings(vX, vY);
458
459 // Create vectors to store the clipped polygon.
460 std::vector<double> cX;
461 std::vector<double> cY;
462
463 // Clip the polygon to the view area.
464 ClipToView(vX, vY, cX, cY);
465
466 // If we have more than 2 vertices, add the polygon.
467 if (cX.size() <= 2) continue;
468 // Again eliminate crossings of the polygon lines.
469 RemoveCrossings(cX, cY);
470
471 // Draw the polygon.
472 std::vector<float> xgr(cX.begin(), cX.end());
473 std::vector<float> ygr(cY.begin(), cY.end());
474 gr.DrawGraph(xgr.size(), xgr.data(), ygr.data(), opt.c_str());
475 } // end z-periodicity loop
476 } // end y-periodicity loop
477 } // end x-periodicity loop
478 } // end loop over elements
479
480}
481
482void ViewFEMesh::DrawElements3d() {
483
484 // Get the map boundaries from the component.
485 double mapxmax = m_cmp->m_mapmax[0];
486 double mapxmin = m_cmp->m_mapmin[0];
487 double mapymax = m_cmp->m_mapmax[1];
488 double mapymin = m_cmp->m_mapmin[1];
489 double mapzmax = m_cmp->m_mapmax[2];
490 double mapzmin = m_cmp->m_mapmin[2];
491
492 // Get the periodicities.
493 double sx = mapxmax - mapxmin;
494 double sy = mapymax - mapymin;
495 double sz = mapzmax - mapzmin;
496 const bool perX = m_cmp->m_periodic[0] || m_cmp->m_mirrorPeriodic[0];
497 const bool perY = m_cmp->m_periodic[1] || m_cmp->m_mirrorPeriodic[1];
498 const bool perZ = m_cmp->m_periodic[2] || m_cmp->m_mirrorPeriodic[2];
499
500 // Set the plot limits.
501 if (m_userBox) {
502 if (std::isinf(m_xMinBox)) m_xMinBox = mapxmin;
503 if (std::isinf(m_yMinBox)) m_yMinBox = mapymin;
504 if (std::isinf(m_zMinBox)) m_zMinBox = mapzmin;
505 if (std::isinf(m_xMaxBox)) m_xMaxBox = mapxmax;
506 if (std::isinf(m_yMaxBox)) m_yMaxBox = mapymax;
507 if (std::isinf(m_zMaxBox)) m_zMaxBox = mapzmax;
508 } else {
509 m_xMinBox = mapxmin;
510 m_yMinBox = mapymin;
511 m_zMinBox = mapzmin;
512 m_xMaxBox = mapxmax;
513 m_yMaxBox = mapymax;
514 m_zMaxBox = mapzmax;
515 }
516 // Determine the number of periods present in the cell.
517 const int nMinX = perX ? int(m_xMinBox / sx) - 1 : 0;
518 const int nMaxX = perX ? int(m_xMaxBox / sx) + 1 : 0;
519 const int nMinY = perY ? int(m_yMinBox / sy) - 1 : 0;
520 const int nMaxY = perY ? int(m_yMaxBox / sy) + 1 : 0;
521 const int nMinZ = perZ ? int(m_zMinBox / sz) - 1 : 0;
522 const int nMaxZ = perZ ? int(m_zMaxBox / sz) + 1 : 0;
523
524 gGeoManager = nullptr;
525 m_geoManager.reset(new TGeoManager("ViewFEMeshGeoManager", ""));
526 TGeoMaterial* matVacuum = new TGeoMaterial("Vacuum", 0., 0., 0.);
527 TGeoMedium* medVacuum = new TGeoMedium("Vacuum", 1, matVacuum);
528 m_media.push_back(medVacuum);
529 // Use silicon as "default" material.
530 TGeoMaterial* matDefault = new TGeoMaterial("Default", 28.085, 14., 2.329);
531 TGeoMedium* medDefault = new TGeoMedium("Default", 1, matDefault);
532 const double hxw = std::max(std::abs(m_xMaxBox), std::abs(m_xMinBox));
533 const double hyw = std::max(std::abs(m_yMaxBox), std::abs(m_yMinBox));
534 const double hzw = std::max(std::abs(m_zMaxBox), std::abs(m_zMinBox));
535 TGeoVolume* top = m_geoManager->MakeBox("Top", medVacuum, hxw, hyw, hzw);
536 m_geoManager->SetTopVolume(top);
537 m_volumes.push_back(top);
538
539 // Loop over all elements.
540 const auto nElements = m_cmp->GetNumberOfElements();
541 for (size_t i = 0; i < nElements; ++i) {
542 size_t mat = 0;
543 bool driftmedium = false;
544 std::vector<size_t> nodes;
545 if (!m_cmp->GetElement(i, mat, driftmedium, nodes)) continue;
546 // Do not plot the drift medium.
547 if (driftmedium && !m_plotMeshBorders) continue;
548 // Do not create polygons for disabled materials.
549 if (m_disabledMaterial.count(mat) > 0 && m_disabledMaterial[mat]) {
550 continue;
551 }
552 const short col = m_colorMap.count(mat) != 0 ? m_colorMap[mat] : 1;
553
554 // Get the vertex coordinates in the basic cell.
555 const size_t nNodes = nodes.size();
556 if (nNodes != 4) continue;
557 std::vector<std::array<double, 3> > v0;
558 for (size_t j = 0; j < nNodes; ++j) {
559 double xn = 0., yn = 0., zn = 0.;
560 if (!m_cmp->GetNode(nodes[j], xn, yn, zn)) break;
561 v0.push_back({xn, yn, zn});
562 }
563 if (v0.size() != nNodes) {
564 std::cerr << m_className << "::DrawElements3d:\n"
565 << " Error retrieving nodes of element " << i << ".\n";
566 continue;
567 }
568 // Coordinates of vertices
569 std::array<std::array<double, 3>, 4> v;
570 // Loop over the periodicities in x.
571 for (int nx = nMinX; nx <= nMaxX; nx++) {
572 const double dx = sx * nx;
573 // Determine the x-coordinates of the vertices.
574 if (m_cmp->m_mirrorPeriodic[0] && nx != 2 * (nx / 2)) {
575 for (size_t j = 0; j < nNodes; ++j) {
576 v[j][0] = mapxmin + (mapxmax - v0[j][0]) + dx;
577 }
578 } else {
579 for (size_t j = 0; j < nNodes; ++j) {
580 v[j][0] = v0[j][0] + dx;
581 }
582 }
583
584 // Loop over the periodicities in y.
585 for (int ny = nMinY; ny <= nMaxY; ny++) {
586 const double dy = sy * ny;
587 // Determine the y-coordinates of the vertices.
588 if (m_cmp->m_mirrorPeriodic[1] && ny != 2 * (ny / 2)) {
589 for (size_t j = 0; j < nNodes; ++j) {
590 v[j][1] = mapymin + (mapymax - v0[j][1]) + dy;
591 }
592 } else {
593 for (size_t j = 0; j < nNodes; ++j) {
594 v[j][1] = v0[j][1] + dy;
595 }
596 }
597
598 // Loop over the periodicities in z.
599 for (int nz = nMinZ; nz <= nMaxZ; nz++) {
600 const double dz = sz * nz;
601 // Determine the z-coordinates of the vertices.
602 if (m_cmp->m_mirrorPeriodic[2] && nz != 2 * (nz / 2)) {
603 for (size_t j = 0; j < nNodes; ++j) {
604 v[j][2] = mapzmin + (mapzmax - v0[j][2]) + dz;
605 }
606 } else {
607 for (size_t j = 0; j < nNodes; ++j) {
608 v[j][2] = v0[j][2] + dz;
609 }
610 }
611 auto tet = new TGeoTet("Tet", v);
612 std::string vname = "Tet" + std::to_string(m_volumes.size());
613 TGeoVolume* vol = new TGeoVolume(vname.c_str(), tet, medDefault);
614 vol->SetLineColor(col);
615 vol->SetTransparency(70.);
616 m_volumes.push_back(vol);
617 top->AddNodeOverlap(vol, 1, gGeoIdentity);
618 }
619 }
620 }
621 }
622 m_geoManager->CloseGeometry();
623 top->SetTransparency(100.);
624 m_geoManager->SetTopVisible();
625 m_geoManager->SetMaxVisNodes(m_volumes.size() + 1);
626 m_geoManager->GetTopNode()->Draw("e");
627}
628
629void ViewFEMesh::DrawDriftLines2d() {
630
631 if (!m_viewDrift) return;
632 // Plot a 2D projection of the drift line.
633 for (const auto& driftLine : m_viewDrift->m_driftLines) {
634 TGraph gr;
635 if (driftLine.second == Particle::Electron) {
636 gr.SetLineColor(m_viewDrift->m_colElectron);
637 } else if (driftLine.second == Particle::Hole) {
638 gr.SetLineColor(m_viewDrift->m_colHole);
639 } else {
640 gr.SetLineColor(m_viewDrift->m_colIon);
641 }
642 std::vector<float> xgr;
643 std::vector<float> ygr;
644 // Loop over the points.
645 for (const auto& point : driftLine.first) {
646 // Project this point onto the plane.
647 float xp = 0., yp = 0.;
648 ToPlane(point[0], point[1], point[2], xp, yp);
649 // Add this point if it is within the view.
650 if (InView(xp, yp)) {
651 xgr.push_back(xp);
652 ygr.push_back(yp);
653 }
654 }
655 if (!xgr.empty()) {
656 gr.DrawGraph(xgr.size(), xgr.data(), ygr.data(), "lsame");
657 }
658 }
659
660}
661
662void ViewFEMesh::DrawDriftLines3d() {
663
664 if (!m_viewDrift) return;
665 for (const auto& driftLine : m_viewDrift->m_driftLines) {
666 std::vector<float> points;
667 for (const auto& p : driftLine.first) {
668 points.push_back(p[0]);
669 points.push_back(p[1]);
670 points.push_back(p[2]);
671 }
672 const int nP = driftLine.first.size();
673 TPolyLine3D pl(nP, points.data());
674 if (driftLine.second == Particle::Electron) {
675 pl.SetLineColor(m_viewDrift->m_colElectron);
676 } else if (driftLine.second == Particle::Hole) {
677 pl.SetLineColor(m_viewDrift->m_colHole);
678 } else {
679 pl.SetLineColor(m_viewDrift->m_colIon);
680 }
681 pl.SetLineWidth(1);
682 pl.DrawPolyLine(nP, points.data(), "same");
683 }
684}
685
686void ViewFEMesh::DrawCST(ComponentCST* cst) {
687 /*The method is based on ViewFEMesh::Draw, thus the first part is copied from
688 * there.
689 * At the moment only x-y, x-z, and y-z are available due to the simple
690 * implementation.
691 * The advantage of this method is that there is no element loop and thus it
692 * is much
693 * faster.
694 */
695
696 // Helper struct.
697 struct PolygonInfo {
698 double p1[2];
699 double p2[2];
700 double p3[2];
701 double p4[2];
702 int element;
703 };
704
705 // Get the map boundaries from the component
706 double mapxmax = m_cmp->m_mapmax[0];
707 double mapxmin = m_cmp->m_mapmin[0];
708 double mapymax = m_cmp->m_mapmax[1];
709 double mapymin = m_cmp->m_mapmin[1];
710 double mapzmax = m_cmp->m_mapmax[2];
711 double mapzmin = m_cmp->m_mapmin[2];
712
713 // Get the periodicities.
714 double sx = mapxmax - mapxmin;
715 double sy = mapymax - mapymin;
716 double sz = mapzmax - mapzmin;
717 const bool perX = m_cmp->m_periodic[0] || m_cmp->m_mirrorPeriodic[0];
718 const bool perY = m_cmp->m_periodic[1] || m_cmp->m_mirrorPeriodic[1];
719 const bool perZ = m_cmp->m_periodic[2] || m_cmp->m_mirrorPeriodic[2];
720
721 // Determine the number of periods present in the cell.
722 const int nMinX = perX ? int(m_xMinBox / sx) - 1 : 0;
723 const int nMaxX = perX ? int(m_xMaxBox / sx) + 1 : 0;
724 const int nMinY = perY ? int(m_yMinBox / sy) - 1 : 0;
725 const int nMaxY = perY ? int(m_yMaxBox / sy) + 1 : 0;
726 const int nMinZ = perZ ? int(m_zMinBox / sz) - 1 : 0;
727 const int nMaxZ = perZ ? int(m_zMaxBox / sz) + 1 : 0;
728
729 std::vector<PolygonInfo> elements;
730 int nMinU = 0, nMaxU = 0, nMinV = 0, nMaxV = 0;
731 double mapumin = 0., mapumax = 0., mapvmin = 0., mapvmax = 0.;
732 double su = 0., sv = 0.;
733 bool mirroru = false, mirrorv = false;
734 double uMin, vMin, uMax, vMax;
735 unsigned int n_x, n_y, n_z;
736 cst->GetNumberOfMeshLines(n_x, n_y, n_z);
737
738 const double fx = m_plane[0];
739 const double fy = m_plane[1];
740 const double fz = m_plane[2];
741 constexpr double eps = 1.e-8;
742 if (fabs(fx) < eps && fabs(fy) < eps && fabs(fz - 1.) < eps) {
743 // xy view
744 std::cout << m_className << "::DrawCST: Creating x-y mesh view.\n";
745 // Calculate the z position.
746 unsigned int i, j, z;
747 const double z0 = m_plane[3] * fz;
748 if (!cst->Coordinate2Index(0, 0, z0, i, j, z)) {
749 std::cerr << " Could not determine the z-index of the plane.\n";
750 return;
751 }
752 std::cout << " The z-index of the plane is " << z << ".\n";
753 nMinU = nMinX;
754 nMaxU = nMaxX;
755 nMinV = nMinY;
756 nMaxV = nMaxY;
757 uMin = m_xMinBox;
758 uMax = m_xMaxBox;
759 vMin = m_yMinBox;
760 vMax = m_yMaxBox;
761
762 mapumin = mapxmin;
763 mapumax = mapxmax;
764 mapvmin = mapymin;
765 mapvmax = mapymax;
766 su = sx;
767 sv = sy;
768 mirroru = perX;
769 mirrorv = perY;
770 for (unsigned int y = 0; y < (n_y - 1); y++) {
771 for (unsigned int x = 0; x < (n_x - 1); x++) {
772 auto elem = cst->Index2Element(x, y, z);
773 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
774 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
775 e_zmin, e_zmax);
776 PolygonInfo tmp_info;
777 tmp_info.element = elem;
778 tmp_info.p1[0] = e_xmin;
779 tmp_info.p2[0] = e_xmax;
780 tmp_info.p3[0] = e_xmax;
781 tmp_info.p4[0] = e_xmin;
782 tmp_info.p1[1] = e_ymin;
783 tmp_info.p2[1] = e_ymin;
784 tmp_info.p3[1] = e_ymax;
785 tmp_info.p4[1] = e_ymax;
786 elements.push_back(std::move(tmp_info));
787 }
788 }
789 } else if (fabs(fx) < eps && fabs(fy + 1.) < eps && fabs(fz) < eps) {
790 // xz-view
791 std::cout << m_className << "::DrawCST: Creating x-z mesh view.\n";
792 // Calculate the y position.
793 unsigned int i = 0, j = 0, y = 0;
794 const double y0 = m_plane[3] * fy;
795 if (!cst->Coordinate2Index(0, y0, 0, i, y, j)) {
796 std::cerr << " Could not determine the y-index of the plane.\n";
797 return;
798 }
799 std::cout << " The y-index of the plane is " << y << ".\n";
800
801 nMinU = nMinX;
802 nMaxU = nMaxX;
803 nMinV = nMinZ;
804 nMaxV = nMaxZ;
805 uMin = m_xMinBox;
806 uMax = m_xMaxBox;
807 vMin = m_zMinBox;
808 vMax = m_zMaxBox;
809
810 mapumin = mapxmin;
811 mapumax = mapxmax;
812 mapvmin = mapzmin;
813 mapvmax = mapzmax;
814 su = sx;
815 sv = sz;
816 mirroru = perX;
817 mirrorv = perZ;
818 for (unsigned int z = 0; z < (n_z - 1); z++) {
819 for (unsigned int x = 0; x < (n_x - 1); x++) {
820 auto elem = cst->Index2Element(x, y, z);
821 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
822 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
823 e_zmin, e_zmax);
824 PolygonInfo tmp_info;
825 tmp_info.element = elem;
826 tmp_info.p1[0] = e_xmin;
827 tmp_info.p2[0] = e_xmax;
828 tmp_info.p3[0] = e_xmax;
829 tmp_info.p4[0] = e_xmin;
830 tmp_info.p1[1] = e_zmin;
831 tmp_info.p2[1] = e_zmin;
832 tmp_info.p3[1] = e_zmax;
833 tmp_info.p4[1] = e_zmax;
834 elements.push_back(std::move(tmp_info));
835 }
836 }
837 } else if (fabs(fx + 1.) < eps && fabs(fy) < eps && fabs(fz) < eps) {
838 // yz-view
839 std::cout << m_className << "::DrawCST: Creating z-y mesh view.\n";
840 // Calculate the x position.
841 unsigned int i, j, x;
842 const double x0 = m_plane[3] * fx;
843 if (!cst->Coordinate2Index(x0, 0, 0, x, i, j)) {
844 std::cerr << " Could not determine the x-index of the plane.\n";
845 return;
846 }
847 std::cout << " The x-index of the plane is " << x << ".\n";
848 nMinU = nMinZ;
849 nMaxU = nMaxZ;
850 nMinV = nMinY;
851 nMaxV = nMaxY;
852 uMin = m_yMinBox;
853 uMax = m_yMaxBox;
854 vMin = m_zMinBox;
855 vMax = m_zMaxBox;
856
857 mapumin = mapzmin;
858 mapumax = mapzmax;
859 mapvmin = mapymin;
860 mapvmax = mapymax;
861 su = sz;
862 sv = sy;
863 mirroru = perZ;
864 mirrorv = perY;
865 for (unsigned int z = 0; z < (n_z - 1); z++) {
866 for (unsigned int y = 0; y < (n_y - 1); y++) {
867 auto elem = cst->Index2Element(x, y, z);
868 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
869 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
870 e_zmin, e_zmax);
871 PolygonInfo tmp_info;
872 tmp_info.element = elem;
873 tmp_info.p1[0] = e_zmin;
874 tmp_info.p2[0] = e_zmax;
875 tmp_info.p3[0] = e_zmax;
876 tmp_info.p4[0] = e_zmin;
877 tmp_info.p1[1] = e_ymin;
878 tmp_info.p2[1] = e_ymin;
879 tmp_info.p3[1] = e_ymax;
880 tmp_info.p4[1] = e_ymax;
881 // Add the polygon to the mesh
882 elements.push_back(std::move(tmp_info));
883 }
884 }
885 } else {
886 std::cerr << m_className << "::DrawCST:\n";
887 std::cerr << " The given plane name is not known.\n";
888 std::cerr << " Please choose one of the following: xy, xz, yz.\n";
889 return;
890 }
891
892 std::cout << m_className << "::DrawCST:\n " << elements.size()
893 << " elements in the projection of the unit cell.\n";
894 for (const auto& element : elements) {
895 size_t mat = 0;
896 bool driftmedium = false;
897 std::vector<size_t> nodes;
898 cst->GetElement(element.element, mat, driftmedium, nodes);
899 // Do not plot the drift medium.
900 if (driftmedium && !m_plotMeshBorders) continue;
901 // Do not create polygons for disabled materials.
902 if (m_disabledMaterial[mat]) continue;
903 TGraph gr;
904 const short col = m_colorMap.count(mat) > 0 ? m_colorMap[mat] : 1;
905 gr.SetLineColor(col);
906 if (m_colorMap_fill.count(mat) > 0) {
907 gr.SetFillColor(m_colorMap_fill[mat]);
908 } else {
909 gr.SetFillColor(col);
910 }
911 if (m_plotMeshBorders)
912 gr.SetLineWidth(3);
913 else
914 gr.SetLineWidth(1);
915
916 for (int nu = nMinU; nu <= nMaxU; nu++) {
917 for (int nv = nMinV; nv <= nMaxV; nv++) {
918 // Add 4 points of the square
919 float tmp_u[4], tmp_v[4];
920 if (mirroru && nu != 2 * (nu / 2)) {
921 // nu is odd
922 tmp_u[0] = mapumin + (mapumax - element.p1[0]) + su * nu;
923 tmp_u[1] = mapumin + (mapumax - element.p2[0]) + su * nu;
924 tmp_u[2] = mapumin + (mapumax - element.p3[0]) + su * nu;
925 tmp_u[3] = mapumin + (mapumax - element.p4[0]) + su * nu;
926 } else {
927 // nu is even
928 tmp_u[0] = element.p1[0] + su * nu;
929 tmp_u[1] = element.p2[0] + su * nu;
930 tmp_u[2] = element.p3[0] + su * nu;
931 tmp_u[3] = element.p4[0] + su * nu;
932 }
933 if (mirrorv && nv != 2 * (nv / 2)) {
934 tmp_v[0] = mapvmin + (mapvmax - element.p1[1]) + sv * nv;
935 tmp_v[1] = mapvmin + (mapvmax - element.p2[1]) + sv * nv;
936 tmp_v[2] = mapvmin + (mapvmax - element.p3[1]) + sv * nv;
937 tmp_v[3] = mapvmin + (mapvmax - element.p4[1]) + sv * nv;
938 } else {
939 tmp_v[0] = element.p1[1] + sv * nv;
940 tmp_v[1] = element.p2[1] + sv * nv;
941 tmp_v[2] = element.p3[1] + sv * nv;
942 tmp_v[3] = element.p4[1] + sv * nv;
943 }
944 if (tmp_u[0] < uMin || tmp_u[1] > uMax || tmp_v[0] < vMin ||
945 tmp_v[2] > vMax) {
946 continue;
947 }
948 std::string opt = "";
949 if (m_plotMeshBorders || !m_fillMesh) opt += "l";
950 if (m_fillMesh) opt += "f";
951 opt += "same";
952 gr.DrawGraph(4, tmp_u, tmp_v, opt.c_str());
953 }
954 }
955 }
956
957}
958
959// Removes duplicate points and line crossings by correctly ordering
960// the points in the provided vectors.
961//
962// NOTE: This is a 2D version of the BUTFLD method in Garfield. It
963// follows the same general algorithm.
964//
965// TODO: there is an algorithm which always sorts points correctly using cross
966// product, see IntersectPlaneArea.
967void ViewFEMesh::RemoveCrossings(std::vector<double>& x,
968 std::vector<double>& y) {
969 // Determine element dimensions
970 double xmin = x[0], xmax = x[0];
971 double ymin = y[0], ymax = y[0];
972 for (int i = 1; i < (int)x.size(); i++) {
973 if (x[i] < xmin) xmin = x[i];
974 if (x[i] > xmax) xmax = x[i];
975 if (y[i] < ymin) ymin = y[i];
976 if (y[i] > ymax) ymax = y[i];
977 }
978
979 // First remove duplicate points
980 double xtol = 1e-10 * std::abs(xmax - xmin);
981 double ytol = 1e-10 * std::abs(ymax - ymin);
982 for (int i = 0; i < (int)x.size(); i++) {
983 for (int j = i + 1; j < (int)x.size(); j++) {
984 if (std::abs(x[i] - x[j]) < xtol && std::abs(y[i] - y[j]) < ytol) {
985 x.erase(x.begin() + j);
986 y.erase(y.begin() + j);
987 j--;
988 }
989 }
990 }
991
992 // No crossings with 3 points or less
993 if (x.size() <= 3) return;
994
995 // Save the polygon size so it is easily accessible
996 int NN = x.size();
997
998 // Keep track of the number of attempts
999 int attempts = 0;
1000
1001 // Exchange points until crossings are eliminated or we have attempted NN
1002 // times
1003 bool crossings = true;
1004 while (crossings && (attempts < NN)) {
1005 // Assume we are done after this attempt.
1006 crossings = false;
1007
1008 for (int i = 1; i <= NN; i++) {
1009 for (int j = i + 2; j <= NN; j++) {
1010 // End the j-loop if we have surpassed N and wrapped around to i
1011 if ((j + 1) > NN && 1 + (j % NN) >= i) break;
1012 // Otherwise, detect crossings and attempt to eliminate them.
1013 // Determine if we have a crossing.
1014 double xc = 0., yc = 0.;
1015 if (!LinesCrossed(x[(i - 1) % NN], y[(i - 1) % NN], x[i % NN],
1016 y[i % NN], x[(j - 1) % NN], y[(j - 1) % NN],
1017 x[j % NN], y[j % NN], xc, yc)) {
1018 continue;
1019 }
1020 // Swap each point from i towards j with each corresponding point
1021 // from j towards i.
1022 for (int k = 1; k <= (j - i) / 2; k++) {
1023 double xs = x[(i + k - 1) % NN];
1024 double ys = y[(i + k - 1) % NN];
1025 x[(i + k - 1) % NN] = x[(j - k) % NN];
1026 y[(i + k - 1) % NN] = y[(j - k) % NN];
1027 x[(j - k) % NN] = xs;
1028 y[(j - k) % NN] = ys;
1029
1030 // Force another attempt
1031 crossings = true;
1032 }
1033 } // end loop over j
1034 } // end loop over i
1035
1036 // Increment the number of attempts
1037 attempts++;
1038
1039 } // end while(crossings)
1040
1041 if (attempts > NN) {
1042 std::cerr << m_className << "::RemoveCrossings:\n Warning: "
1043 << "Maximum attempts reached. Crossings not removed.\n";
1044 }
1045}
1046
1047/// Return true if the specified point is in the view region.
1048bool ViewFEMesh::InView(const double x, const double y) const {
1049 // Test whether this vertex is inside the view.
1050 if (m_userPlotLimits) {
1051 return (x >= m_xMinPlot && x <= m_xMaxPlot &&
1052 y >= m_yMinPlot && y <= m_yMaxPlot);
1053 }
1054 bool edge = false;
1055 return IsInPolygon(x, y, m_viewRegionX, m_viewRegionY, edge);
1056}
1057
1058//
1059// Determines whether the line connecting points (x1,y1) and (x2,y2)
1060// and the line connecting points (u1,v1) and (u2,v2) cross somewhere
1061// between the 4 points. Sets the crossing point in (xc, yc).
1062//
1063// Ported from Garfield function CROSSD
1064//
1065bool ViewFEMesh::LinesCrossed(double x1, double y1, double x2, double y2,
1066 double u1, double v1, double u2, double v2,
1067 double& xc, double& yc) const {
1068 // Set the tolerances.
1069 double xtol = 1.0e-10 * std::max({std::abs(x1), std::abs(x2), std::abs(u1),
1070 std::abs(u2)});
1071 double ytol = 1.0e-10 * std::max({std::abs(y1), std::abs(y2), std::abs(v1),
1072 std::abs(v2)});
1073 if (xtol <= 0) xtol = 1.0e-10;
1074 if (ytol <= 0) ytol = 1.0e-10;
1075
1076 // Compute the distances and determinant (dx,dy) x (du,dv).
1077 double dy = y2 - y1;
1078 double dv = v2 - v1;
1079 double dx = x1 - x2;
1080 double du = u1 - u2;
1081 double det = dy * du - dx * dv;
1082
1083 // Check for crossing because one of the endpoints is on both lines.
1084 if (OnLine(x1, y1, x2, y2, u1, v1)) {
1085 xc = u1;
1086 yc = v1;
1087 return true;
1088 } else if (OnLine(x1, y1, x2, y2, u2, v2)) {
1089 xc = u2;
1090 yc = v2;
1091 return true;
1092 } else if (OnLine(u1, v1, u2, v2, x1, y1)) {
1093 xc = x1;
1094 yc = y1;
1095 return true;
1096 } else if (OnLine(u1, v1, u2, v2, x2, y2)) {
1097 xc = x2;
1098 yc = y2;
1099 return true;
1100 }
1101 // Check if the lines are parallel (zero determinant).
1102 if (std::abs(det) < xtol * ytol) return false;
1103 // No special case: compute point of intersection.
1104
1105 // Solve crossing equations.
1106 xc = (du * (x1 * y2 - x2 * y1) - dx * (u1 * v2 - u2 * v1)) / det;
1107 yc = ((-1 * dv) * (x1 * y2 - x2 * y1) + dy * (u1 * v2 - u2 * v1)) / det;
1108
1109 // Determine if this point is on both lines.
1110 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc))
1111 return true;
1112
1113 // The lines do not cross if we have reached this point.
1114 return false;
1115}
1116
1117// Determines whether the point (u,v) lies on the line connecting
1118// points (x1,y1) and (x2,y2).
1119//
1120// Ported from Garfield function ONLIND
1121//
1122bool ViewFEMesh::OnLine(double x1, double y1, double x2, double y2, double u,
1123 double v) const {
1124 // Set the tolerances
1125 double xtol = 1.e-10 * std::max({std::abs(x1), std::abs(x2), std::abs(u)});
1126 double ytol = 1.e-10 * std::max({std::abs(y1), std::abs(y2), std::abs(v)});
1127 if (xtol <= 0) xtol = 1.0e-10;
1128 if (ytol <= 0) ytol = 1.0e-10;
1129
1130 // To store the coordinates of the comparison point
1131 double xc = 0, yc = 0;
1132
1133 // Check if (u,v) is the point (x1,y1) or (x2,y2)
1134 if ((std::abs(x1 - u) <= xtol && std::abs(y1 - v) <= ytol) ||
1135 (std::abs(x2 - u) <= xtol && std::abs(y2 - v) <= ytol)) {
1136 return true;
1137 }
1138 // Check if the line is actually a point
1139 if (std::abs(x1 - x2) <= xtol && std::abs(y1 - y2) <= ytol) {
1140 return false;
1141 }
1142 // Choose (x1,y1) as starting point if closer to (u,v)
1143 if (std::abs(u - x1) + std::abs(v - y1) <
1144 std::abs(u - x2) + std::abs(v - y2)) {
1145 // Compute the component of the line from (x1,y1) to (u,v)
1146 // along the line from (x1,y1) to (x2,y2)
1147 double dpar = ((u - x1) * (x2 - x1) + (v - y1) * (y2 - y1)) /
1148 ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
1149
1150 // Determine the point on the line to which to compare (u,v)
1151 if (dpar < 0.0) {
1152 xc = x1;
1153 yc = y1;
1154 } else if (dpar > 1.0) {
1155 xc = x2;
1156 yc = y2;
1157 } else {
1158 xc = x1 + dpar * (x2 - x1);
1159 yc = y1 + dpar * (y2 - y1);
1160 }
1161 } else {
1162 // Choose (x2,y2) as starting point if closer to (u,v)
1163 // Compute the component of the line from (x2,y2) to (u,v)
1164 // along the line from (x2,y2) to (x1,y1)
1165 double dpar = ((u - x2) * (x1 - x2) + (v - y2) * (y1 - y2)) /
1166 ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
1167
1168 // Determine the point on the line to which to compare (u,v)
1169 if (dpar < 0.0) {
1170 xc = x2;
1171 yc = y2;
1172 } else if (dpar > 1.0) {
1173 xc = x1;
1174 yc = y1;
1175 } else {
1176 xc = x2 + dpar * (x1 - x2);
1177 yc = y2 + dpar * (y1 - y2);
1178 }
1179 }
1180
1181 // Compare the calculated point to (u,v)
1182 if (std::abs(u - xc) < xtol && std::abs(v - yc) < ytol) return true;
1183
1184 return false;
1185}
1186
1187// Ported from Garfield: determines the point of intersection, in planar
1188// coordinates, of a plane with the line connecting multiple points
1189// x1,y1,z1;x2,y2,z2: the world coordinates of the two points
1190// projMat;planeMat: the projection and plane matrices
1191// xMat: the resulting planar coordinates of the intersection point
1192bool ViewFEMesh::PlaneCut(double x1, double y1, double z1, double x2, double y2,
1193 double z2, TMatrixD& xMat) {
1194 // Set up the matrix for cutting edges not in the plane
1195 TArrayD dataCut(9);
1196 TMatrixD cutMat(3, 3);
1197 dataCut[0] = m_proj[0][0];
1198 dataCut[1] = m_proj[1][0];
1199 dataCut[2] = x1 - x2;
1200 dataCut[3] = m_proj[0][1];
1201 dataCut[4] = m_proj[1][1];
1202 dataCut[5] = y1 - y2;
1203 dataCut[6] = m_proj[0][2];
1204 dataCut[7] = m_proj[1][2];
1205 dataCut[8] = z1 - z2;
1206 cutMat.SetMatrixArray(dataCut.GetArray());
1207
1208 // Calculate the determinant of the cut matrix
1209 double cutDet =
1210 cutMat(0, 0) *
1211 (cutMat(1, 1) * cutMat(2, 2) - cutMat(1, 2) * cutMat(2, 1)) -
1212 cutMat(0, 1) *
1213 (cutMat(1, 0) * cutMat(2, 2) - cutMat(1, 2) * cutMat(2, 0)) +
1214 cutMat(0, 2) *
1215 (cutMat(1, 0) * cutMat(2, 1) - cutMat(1, 1) * cutMat(2, 0));
1216
1217 // Do not proceed if the matrix is singular
1218 if (std::abs(cutDet) < 1e-20) return false;
1219
1220 // Set up a coordinate vector (RHS of equation)
1221 TArrayD dataCoords(3);
1222 TMatrixD coordMat(3, 1);
1223 dataCoords[0] = x1 - m_proj[2][0];
1224 dataCoords[1] = y1 - m_proj[2][1];
1225 dataCoords[2] = z1 - m_proj[2][2];
1226 coordMat.SetMatrixArray(dataCoords.GetArray());
1227
1228 // Invert the cut matrix and multiply to get the solution
1229 cutMat.SetTol(1e-20);
1230 cutMat.Invert();
1231 // Do not proceed if the matrix is singular
1232 if (!cutMat.IsValid()) return false;
1233 xMat = cutMat * coordMat;
1234
1235 // Return success if the plane point is between the two vertices
1236 if (xMat(2, 0) < 0 || xMat(2, 0) > 1) return false;
1237 return true;
1238}
1239
1240// Calculates view region and canvas dimensions based on projection plane
1241// and view area
1242bool ViewFEMesh::IntersectPlaneArea(double& xmin, double& ymin,
1243 double& xmax, double& ymax) {
1244 std::vector<TMatrixD> intersect_points;
1245 m_viewRegionX.clear();
1246 m_viewRegionY.clear();
1247 // Loop over box edges
1248 for (int i0 = 0; i0 < 2; ++i0) {
1249 for (int j0 = 0; j0 < 2; ++j0) {
1250 for (int k0 = 0; k0 < 2; ++k0) {
1251 for (int i1 = i0; i1 < 2; ++i1) {
1252 for (int j1 = j0; j1 < 2; ++j1) {
1253 for (int k1 = k0; k1 < 2; ++k1) {
1254 if (i1 - i0 + j1 - j0 + k1 - k0 != 1) continue;
1255 const double x0 = i0 ? m_xMinBox : m_xMaxBox;
1256 const double y0 = j0 ? m_yMinBox : m_yMaxBox;
1257 const double z0 = k0 ? m_zMinBox : m_zMaxBox;
1258 const double x1 = i1 ? m_xMinBox : m_xMaxBox;
1259 const double y1 = j1 ? m_yMinBox : m_yMaxBox;
1260 const double z1 = k1 ? m_zMinBox : m_zMaxBox;
1261 TMatrixD xMat(3, 1);
1262 if (!PlaneCut(x0, y0, z0, x1, y1, z1, xMat)) continue;
1263 if (m_debug) {
1264 std::cout << m_className << "::IntersectPlaneArea:\n"
1265 << " Intersection of plane at (" << xMat(0, 0)
1266 << ", " << xMat(1, 0) << ", " << xMat(2, 0)
1267 << ") with edge\n ("
1268 << x0 << ", " << y0 << ", " << z0 << ")-("
1269 << x1 << ", " << y1 << ", " << z1 << ")\n";
1270 }
1271 // Do not add same points (the case when plane contains an edge)
1272 bool skip = false;
1273 for (auto& p : intersect_points) {
1274 const double dx = xMat(0, 0) - p(0, 0);
1275 const double dy = xMat(1, 0) - p(1, 0);
1276 if (std::hypot(dx, dy) < 1e-10) {
1277 skip = true;
1278 break;
1279 }
1280 }
1281 if (!skip) intersect_points.push_back(xMat);
1282 }
1283 }
1284 }
1285 }
1286 }
1287 }
1288 if (intersect_points.size() < 3) {
1289 std::cerr << m_className << "::IntersectPlaneArea:\n"
1290 << " WARNING: Empty intersection of view plane with area.\n";
1291 return false;
1292 }
1293 TMatrixD offset = intersect_points[0];
1294 xmin = xmax = intersect_points[0](0, 0);
1295 ymin = ymax = intersect_points[0](1, 0);
1296 // Remove crossings by sorting points rotation-wise.
1297 for (auto& p : intersect_points) p -= offset;
1298 std::sort(intersect_points.begin(), intersect_points.end(),
1299 [](const TMatrixD& a, const TMatrixD& b) -> bool {
1300 double cross_z = a(0, 0) * b(1, 0) - a(1, 0) * b(0, 0);
1301 return cross_z < 0;
1302 });
1303 for (auto& p : intersect_points) {
1304 p += offset;
1305 m_viewRegionX.push_back(p(0, 0));
1306 m_viewRegionY.push_back(p(1, 0));
1307 xmin = std::min(p(0, 0), xmin);
1308 ymin = std::min(p(1, 0), ymin);
1309 xmax = std::max(p(0, 0), xmax);
1310 ymax = std::max(p(1, 0), ymax);
1311 }
1312 return true;
1313}
1314
1315// Ported from Garfield (function INTERD):
1316// Returns true if the point (x,y) is inside of the specified polygon.
1317// x: the x-coordinate
1318// y: the y-coordinate
1319// px: the x-vertices of the polygon
1320// py: the y-vertices of the polygon
1321// edge: a variable set to true if the point is located on the polygon edge
1322bool ViewFEMesh::IsInPolygon(double x, double y,
1323 const std::vector<double>& px,
1324 const std::vector<double>& py, bool& edge) const {
1325 // Get the number and coordinates of the polygon vertices.
1326 const size_t pN = px.size();
1327
1328 // Handle the special case of less than 2 vertices.
1329 if (pN < 2) return false;
1330 // Handle the special case of exactly 2 vertices (a line).
1331 if (pN == 2) return OnLine(px[0], py[0], px[1], py[1], x, y);
1332
1333 // Set the minimum and maximum coordinates of all polygon vertices.
1334 double px_min = px[0], py_min = py[0];
1335 double px_max = px[0], py_max = py[0];
1336 for (size_t i = 0; i < pN; i++) {
1337 px_min = std::min(px_min, px[i]);
1338 py_min = std::min(py_min, py[i]);
1339 px_max = std::max(px_max, px[i]);
1340 py_max = std::max(py_max, py[i]);
1341 }
1342
1343 // Set the tolerances
1344 double xtol = 1.0e-10 * std::max(std::abs(px_min), std::abs(px_max));
1345 double ytol = 1.0e-10 * std::max(std::abs(py_min), std::abs(py_max));
1346 if (xtol <= 0) xtol = 1.0e-10;
1347 if (ytol <= 0) ytol = 1.0e-10;
1348
1349 // If we have essentially one x value, check to see if y is in range.
1350 if (std::abs(px_max - px_min) < xtol) {
1351 edge = (y > (py_min - ytol) && y < (py_max + ytol) &&
1352 std::abs(px_max + px_min - 2 * x) < xtol);
1353 return false;
1354 }
1355 // If we have essentially one y value, check to see if x is in range.
1356 if (std::abs(py_max - py_min) < ytol) {
1357 edge = (x > (px_min - xtol) && x < (px_max + xtol) &&
1358 std::abs(py_max + py_min - 2 * y) < ytol);
1359 return false;
1360 }
1361
1362 // Set "infinity" points.
1363 double xinf = px_min - std::abs(px_max - px_min);
1364 double yinf = py_min - std::abs(py_max - py_min);
1365
1366 // Loop until successful or maximum iterations (100) reached.
1367 int niter = 0;
1368 bool done = false;
1369 int ncross = 0;
1370
1371 while (!done && niter < 100) {
1372 // Assume we will finish on this loop.
1373 done = true;
1374
1375 // Loop over all edges, counting the number of edges crossed by a line
1376 // extending from (x, y) to (xinf, yinf).
1377 ncross = 0;
1378 for (size_t i = 0; (done && i < pN); i++) {
1379 // Determine whether the point lies on the edge.
1380 if (OnLine(px[i % pN], py[i % pN], px[(i + 1) % pN], py[(i + 1) % pN], x,
1381 y)) {
1382 edge = true;
1383 return false;
1384 }
1385
1386 // Determine whether this edge is crossed; if so increment the counter.
1387 double xc = 0., yc = 0.;
1388 if (LinesCrossed(x, y, xinf, yinf, px[i % pN], py[i % pN],
1389 px[(i + 1) % pN], py[(i + 1) % pN], xc, yc))
1390 ncross++;
1391
1392 // Ensure this vertex is not crossed by the line from (x,y)
1393 // to (xinf,yinf); if so recompute (xinf,yinf) and start over.
1394 if (OnLine(x, y, xinf, yinf, px[i], py[i])) {
1395 // Recompute (xinf,yinf).
1396 xinf = px_min - RndmUniform() * std::abs(px_max - xinf);
1397 yinf = py_min - RndmUniform() * std::abs(py_max - yinf);
1398
1399 // Start over.
1400 done = false;
1401 niter++;
1402 }
1403 }
1404 }
1405
1406 // If we failed to finish iterating, return false.
1407 if (niter >= 100) {
1408 std::cerr << m_className << "::IsInPolygon: Unable to determine whether ("
1409 << x << ", " << y << ") is inside a polygon. Returning false.\n";
1410 return false;
1411 }
1412
1413 // Point is inside for an odd, nonzero number of crossings.
1414 return (ncross != 2 * (ncross / 2));
1415}
1416
1417// Ported from Garfield (method GRCONV):
1418// Clip the specified polygon to the view region; return the clipped polygon.
1419// px: the x-vertices of the polygon
1420// py: the y-vertices of the polygon
1421// cx: to contain the x-vertices of the clipped polygon
1422// cy: to contain the y-vertices of the clipped polygon
1423void ViewFEMesh::ClipToView(std::vector<double>& px, std::vector<double>& py,
1424 std::vector<double>& cx, std::vector<double>& cy) {
1425 // Get the number and coordinates of the polygon vertices.
1426 int pN = (int)px.size();
1427
1428 // Clear the vectors to contain the final polygon.
1429 cx.clear();
1430 cy.clear();
1431
1432 // Set up the view vertices.
1433 const auto& vx = m_viewRegionX;
1434 const auto& vy = m_viewRegionY;
1435 const int vN = m_viewRegionX.size();
1436
1437 // Do nothing if we have less than 2 points.
1438 if (pN < 2) return;
1439
1440 // Loop over the polygon vertices.
1441 for (int i = 0; i < pN; i++) {
1442 // Flag for skipping check for edge intersection.
1443 bool skip = false;
1444
1445 // Loop over the view vertices.
1446 for (int j = 0; j < vN; j++) {
1447 // Determine whether this vertex lies on a view edge:
1448 // if so add the vertex to the final polygon.
1449 if (OnLine(vx[j % vN], vy[j % vN], vx[(j + 1) % vN], vy[(j + 1) % vN],
1450 px[i], py[i])) {
1451 // Add the vertex.
1452 cx.push_back(px[i]);
1453 cy.push_back(py[i]);
1454
1455 // Skip edge intersection check in this case.
1456 skip = true;
1457 }
1458
1459 // Determine whether a corner of the view area lies on this edge:
1460 // if so add the corner to the final polygon.
1461 if (OnLine(px[i % pN], py[i % pN], px[(i + 1) % pN], py[(i + 1) % pN],
1462 vx[j], vy[j])) {
1463 // Add the vertex.
1464 cx.push_back(vx[j]);
1465 cy.push_back(vy[j]);
1466
1467 // Skip edge intersection check in this case.
1468 skip = true;
1469 }
1470 }
1471
1472 // If we have not skipped the edge intersection check, look for an
1473 // intersection between this edge and the view edges.
1474 if (skip) continue;
1475 // Loop over the view vertices.
1476 for (int j = 0; j < vN; j++) {
1477 // Check for a crossing with this edge;
1478 // if one exists, add the crossing point.
1479 double xc = 0., yc = 0.;
1480 if (LinesCrossed(vx[j % vN], vy[j % vN], vx[(j + 1) % vN],
1481 vy[(j + 1) % vN], px[i % pN], py[i % pN],
1482 px[(i + 1) % pN], py[(i + 1) % pN], xc, yc)) {
1483 // Add a vertex.
1484 cx.push_back(xc);
1485 cy.push_back(yc);
1486 }
1487 }
1488 }
1489
1490 // Find all view field vertices inside the polygon.
1491 for (int j = 0; j < vN; j++) {
1492 // Test whether this vertex is inside the polygon.
1493 // If so, add it to the final polygon.
1494 bool edge = false;
1495 if (IsInPolygon(vx[j], vy[j], px, py, edge)) {
1496 // Add the view vertex.
1497 cx.push_back(vx[j]);
1498 cy.push_back(vy[j]);
1499 }
1500 }
1501
1502 // Find all polygon vertices inside the box.
1503 for (int i = 0; i < pN; i++) {
1504 // Test whether this vertex is inside the view.
1505 // If so, add it to the final polygon.
1506 bool edge = false;
1507 if (IsInPolygon(px[i], py[i], vx, vy, edge)) {
1508 // Add the polygon vertex.
1509 cx.push_back(px[i]);
1510 cy.push_back(py[i]);
1511 }
1512 }
1513}
1514} // namespace Garfield
Base class for components based on finite-element field maps.
std::array< double, 3 > m_mapmin
virtual size_t GetNumberOfElements() const
Return the number of mesh elements.
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax) const
Return the volume and aspect ratio of a mesh element.
std::array< double, 3 > m_mapmax
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
Definition Component.hh:376
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
Definition Component.hh:374
std::array< double, 4 > m_plane
Definition ViewBase.hh:103
std::string LabelY()
Definition ViewBase.cc:510
static void SetRange(TVirtualPad *pad, const double x0, const double y0, const double x1, const double y1)
Definition ViewBase.cc:93
std::string LabelX()
Definition ViewBase.cc:447
std::string m_className
Definition ViewBase.hh:82
std::array< std::array< double, 3 >, 3 > m_proj
Definition ViewBase.hh:100
TPad * GetCanvas()
Retrieve the canvas.
Definition ViewBase.cc:74
static bool RangeSet(TVirtualPad *)
Definition ViewBase.cc:83
void ToPlane(const T x, const T y, const T z, T &xp, T &yp) const
Definition ViewBase.hh:113
ViewBase()=delete
Default constructor.
virtual void SetPlane(const double fx, const double fy, const double fz, const double x0, const double y0, const double z0)
Definition ViewBase.cc:142
bool Plot(const bool twod=true)
Plot method to be called by user.
Definition ViewFEMesh.cc:60
void CreateDefaultAxes()
Create a default set of custom-made axes.
ViewFEMesh()
Constructor.
Definition ViewFEMesh.cc:22
void SetPlane(const double fx, const double fy, const double fz, const double x0, const double y0, const double z0) override
void SetComponent(ComponentFieldMap *cmp)
Set the component from which to retrieve the mesh and field.
Definition ViewFEMesh.cc:49
~ViewFEMesh()
Destructor.
Definition ViewFEMesh.cc:24
void SetXaxis(TGaxis *ax)
void SetYaxis(TGaxis *ay)
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