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