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