10#include <TPolyLine3D.h>
28void ViewFEMesh::Reset() {
29 for (
auto it = m_volumes.begin(), end = m_volumes.end(); it != end; ++it) {
31 TGeoShape* shape = (*it)->GetShape();
32 if (shape)
delete shape;
37 for (
auto it = m_media.begin(), end = m_media.end(); it != end; ++it) {
39 TGeoMaterial* material = (*it)->GetMaterial();
40 if (material)
delete material;
46 m_geoManager.reset(
nullptr);
51 std::cerr <<
m_className <<
"::SetComponent: Null pointer.\n";
62 std::cerr <<
m_className <<
"::Plot: Component is not defined.\n";
66 double pmin = 0., pmax = 0.;
67 if (!m_cmp->GetVoltageRange(pmin, pmax)) {
68 std::cerr <<
m_className <<
"::Plot: Component is not ready.\n";
78 <<
" Cannot plot 2D mesh elements in 3D.\n";
86 if (!GetPlotLimits())
return false;
93 if (!m_xaxis && !m_yaxis) {
97 if (m_xaxisTitle.empty()) {
98 frame->GetXaxis()->SetTitle(
LabelX().c_str());
100 frame->GetXaxis()->SetTitle(m_xaxisTitle.c_str());
102 if (m_yaxisTitle.empty()) {
103 frame->GetYaxis()->SetTitle(
LabelY().c_str());
105 frame->GetYaxis()->SetTitle(m_yaxisTitle.c_str());
109 if (m_xaxis) m_xaxis->Draw();
110 if (m_yaxis) m_yaxis->Draw();
117 std::cout <<
m_className <<
"::Plot: CST component. Calling DrawCST.\n";
125 if (m_drawViewRegion && !m_viewRegionX.empty()) {
127 poly.SetLineColor(kSpring + 4);
128 poly.SetLineWidth(3);
129 std::vector<double> xv = m_viewRegionX;
130 std::vector<double> yv = m_viewRegionY;
132 xv.push_back(m_viewRegionX[0]);
133 yv.push_back(m_viewRegionY[0]);
134 poly.DrawPolyLine(xv.size(), xv.data(), yv.data(),
"same");
138 gPad->RedrawAxis(
"g");
142bool ViewFEMesh::GetPlotLimits() {
147 std::vector<double> xg(4, 0.);
148 std::vector<double> yg(4, 0.);
149 std::vector<double> zg(4, 0.);
150 for (
size_t i = 0; i < 4; ++i) {
155 m_xMinBox = *std::min_element(xg.begin(), xg.end());
156 m_xMaxBox = *std::max_element(xg.begin(), xg.end());
157 m_yMinBox = *std::min_element(yg.begin(), yg.end());
158 m_yMaxBox = *std::max_element(yg.begin(), yg.end());
159 m_zMinBox = *std::min_element(zg.begin(), zg.end());
160 m_zMaxBox = *std::max_element(zg.begin(), zg.end());
167 if (!m_cmp)
return false;
171 <<
" Bounding box of the component is not defined.\n"
172 <<
" Please set the limits explicitly (SetArea).\n";
178 double x0 = 0., y0 = 0., z0 = 0.;
179 double x1 = 0., y1 = 0., z1 = 0.;
180 if (!m_cmp->GetElementaryCell(x0, y0, z0, x1, y1, z1)) {
182 <<
" Cell boundaries are not defined.\n"
183 <<
" Please set the limits explicitly (SetArea).\n";
200 double xmin = 0., xmax = 0., ymin = 0., ymax = 0.;
201 IntersectPlaneArea(xmin, ymin, xmax, ymax);
202 if (m_viewRegionX.empty()) {
203 std::cerr <<
m_className <<
"::GetPlotLimits: Empty view.\n"
204 <<
" Make sure the viewing plane (SetPlane)\n"
205 <<
" intersects with the bounding box.\n";
216 const double x0,
const double y0,
const double z0) {
217 if (fy * fy + fz * fz > 0) {
218 SetPlane(fx, fy, fz, x0, y0, z0, 1, 0, 0);
220 SetPlane(fx, fy, fz, x0, y0, z0, 0, 1, 0);
225 const double x0,
const double y0,
const double z0,
226 const double hx,
const double hy,
const double hz) {
239 if (!GetPlotLimits()) {
240 std::cerr <<
m_className <<
"::CreateDefaultAxes:\n"
241 <<
" Cannot determine the axis limits.\n";
250 m_xaxis =
new TGaxis(x0, y0, x1, y0, x0, x1, 2405,
"x");
251 m_yaxis =
new TGaxis(x0, y0, x0, y1, y0, y1, 2405,
"y");
254 m_xaxis->SetLabelSize(0.025);
255 m_yaxis->SetLabelSize(0.025);
258 m_xaxis->SetTitleSize(0.03);
259 m_xaxis->SetTitle(
LabelX().c_str());
260 m_yaxis->SetTitleSize(0.03);
261 m_yaxis->SetTitle(
LabelY().c_str());
266void ViewFEMesh::DrawElements2d() {
268 double mapxmax = m_cmp->
m_mapmax[0];
269 double mapxmin = m_cmp->
m_mapmin[0];
270 double mapymax = m_cmp->
m_mapmax[1];
271 double mapymin = m_cmp->
m_mapmin[1];
272 double mapzmax = m_cmp->
m_mapmax[2];
273 double mapzmin = m_cmp->
m_mapmin[2];
276 double sx = mapxmax - mapxmin;
277 double sy = mapymax - mapymin;
278 double sz = mapzmax - mapzmin;
287 const double dist =
m_plane[3];
293 const int nMinX = perX ? int(
m_xMinBox / sx) - 1 : 0;
294 const int nMaxX = perX ? int(
m_xMaxBox / sx) + 1 : 0;
295 const int nMinY = perY ? int(
m_yMinBox / sy) - 1 : 0;
296 const int nMaxY = perY ? int(
m_yMaxBox / sy) + 1 : 0;
297 const int nMinZ = perZ ? int(
m_zMinBox / sz) - 1 : 0;
298 const int nMaxZ = perZ ? int(
m_zMaxBox / sz) + 1 : 0;
305 for (
size_t i = 0; i < nElements; ++i) {
307 bool driftmedium =
false;
308 std::vector<size_t> nodes;
309 if (!m_cmp->
GetElement(i, mat, driftmedium, nodes))
continue;
311 if (driftmedium && !m_plotMeshBorders)
continue;
313 if (m_disabledMaterial.count(mat) > 0 && m_disabledMaterial[mat]) {
317 const short col = m_colorMap.count(mat) != 0 ? m_colorMap[mat] : 1;
318 gr.SetLineColor(col);
319 if (m_colorMap_fill.count(mat) != 0) {
320 gr.SetFillColor(m_colorMap_fill[mat]);
322 gr.SetFillColor(col);
325 std::string opt =
"";
326 if (m_plotMeshBorders || !m_fillMesh) opt +=
"l";
327 if (m_fillMesh) opt +=
"f";
330 std::vector<double> vx0;
331 std::vector<double> vy0;
332 std::vector<double> vz0;
333 const size_t nNodes = nodes.size();
334 for (
size_t j = 0; j < nNodes; ++j) {
335 double xn = 0., yn = 0., zn = 0.;
336 if (!m_cmp->GetNode(nodes[j], xn, yn, zn))
continue;
341 if (vx0.size() != nNodes) {
343 <<
" Error retrieving nodes of element " << i <<
".\n";
347 std::vector<double> vx(nNodes, 0.);
348 std::vector<double> vy(nNodes, 0.);
349 std::vector<double> vz(nNodes, 0.);
351 for (
int nx = nMinX; nx <= nMaxX; nx++) {
352 const double dx = sx * nx;
354 if (m_cmp->m_mirrorPeriodic[0] && nx != 2 * (nx / 2)) {
355 for (
size_t j = 0; j < nNodes; ++j) {
356 vx[j] = mapxmin + (mapxmax - vx0[j]) + dx;
359 for (
size_t j = 0; j < nNodes; ++j) {
365 for (
int ny = nMinY; ny <= nMaxY; ny++) {
366 const double dy = sy * ny;
368 if (m_cmp->m_mirrorPeriodic[1] && ny != 2 * (ny / 2)) {
369 for (
size_t j = 0; j < nNodes; ++j) {
370 vy[j] = mapymin + (mapymax - vy0[j]) + dy;
373 for (
size_t j = 0; j < nNodes; ++j) {
379 for (
int nz = nMinZ; nz <= nMaxZ; nz++) {
380 const double dz = sz * nz;
382 if (m_cmp->m_mirrorPeriodic[2] && nz != 2 * (nz / 2)) {
383 for (
size_t j = 0; j < nNodes; ++j) {
384 vz[j] = mapzmin + (mapzmax - vz0[j]) + dz;
387 for (
size_t j = 0; j < nNodes; ++j) {
393 std::vector<double> vX;
394 std::vector<double> vY;
397 const double pcf = std::max(
398 {std::abs(vx[0]), std::abs(vy[0]), std::abs(vz[0]),
399 std::abs(fx), std::abs(fy), std::abs(fz), std::abs(dist)});
400 const double tol = 1.e-4 * pcf;
402 std::vector<bool> in(nNodes,
false);
404 for (
size_t j = 0; j < nNodes; ++j) {
405 const double d = fx * vx[j] + fy * vy[j] + fz * vz[j] - dist;
406 if (std::abs(d) < tol) {
410 double xp = 0., yp = 0.;
411 ToPlane(vx[j], vy[j], vz[j], xp, yp);
423 if (std::abs(cnt) == (
int)nNodes)
continue;
426 const std::array<std::array<unsigned int, 3>, 8> neighbours = {{
427 {1, 2, 4}, {0, 3, 5}, {0, 3, 6}, {1, 2, 7},
428 {0, 5, 6}, {1, 4, 7}, {2, 4, 7}, {3, 5, 6}
430 for (
size_t j = 0; j < nNodes; ++j) {
431 for (
unsigned int k : neighbours[j]) {
432 if (in[j] || in[k])
continue;
433 if (PlaneCut(vx[j], vy[j], vz[j],
434 vx[k], vy[k], vz[k], xMat)) {
435 vX.push_back(xMat(0, 0));
436 vY.push_back(xMat(1, 0));
442 for (
size_t j = 0; j < nNodes; ++j) {
443 for (
size_t k = j + 1; k < nNodes; ++k) {
444 if (in[j] || in[k])
continue;
445 if (PlaneCut(vx[j], vy[j], vz[j],
446 vx[k], vy[k], vz[k], xMat)) {
447 vX.push_back(xMat(0, 0));
448 vY.push_back(xMat(1, 0));
453 if (vX.size() < 3)
continue;
457 RemoveCrossings(vX, vY);
460 std::vector<double> cX;
461 std::vector<double> cY;
464 ClipToView(vX, vY, cX, cY);
467 if (cX.size() <= 2)
continue;
469 RemoveCrossings(cX, cY);
472 std::vector<float> xgr(cX.begin(), cX.end());
473 std::vector<float> ygr(cY.begin(), cY.end());
474 gr.DrawGraph(xgr.size(), xgr.data(), ygr.data(), opt.c_str());
482void ViewFEMesh::DrawElements3d() {
485 double mapxmax = m_cmp->m_mapmax[0];
486 double mapxmin = m_cmp->m_mapmin[0];
487 double mapymax = m_cmp->m_mapmax[1];
488 double mapymin = m_cmp->m_mapmin[1];
489 double mapzmax = m_cmp->m_mapmax[2];
490 double mapzmin = m_cmp->m_mapmin[2];
493 double sx = mapxmax - mapxmin;
494 double sy = mapymax - mapymin;
495 double sz = mapzmax - mapzmin;
496 const bool perX = m_cmp->m_periodic[0] || m_cmp->m_mirrorPeriodic[0];
497 const bool perY = m_cmp->m_periodic[1] || m_cmp->m_mirrorPeriodic[1];
498 const bool perZ = m_cmp->m_periodic[2] || m_cmp->m_mirrorPeriodic[2];
517 const int nMinX = perX ? int(
m_xMinBox / sx) - 1 : 0;
518 const int nMaxX = perX ? int(
m_xMaxBox / sx) + 1 : 0;
519 const int nMinY = perY ? int(
m_yMinBox / sy) - 1 : 0;
520 const int nMaxY = perY ? int(
m_yMaxBox / sy) + 1 : 0;
521 const int nMinZ = perZ ? int(
m_zMinBox / sz) - 1 : 0;
522 const int nMaxZ = perZ ? int(
m_zMaxBox / sz) + 1 : 0;
524 gGeoManager =
nullptr;
525 m_geoManager.reset(
new TGeoManager(
"ViewFEMeshGeoManager",
""));
526 TGeoMaterial* matVacuum =
new TGeoMaterial(
"Vacuum", 0., 0., 0.);
527 TGeoMedium* medVacuum =
new TGeoMedium(
"Vacuum", 1, matVacuum);
528 m_media.push_back(medVacuum);
530 TGeoMaterial* matDefault =
new TGeoMaterial(
"Default", 28.085, 14., 2.329);
531 TGeoMedium* medDefault =
new TGeoMedium(
"Default", 1, matDefault);
535 TGeoVolume* top = m_geoManager->MakeBox(
"Top", medVacuum, hxw, hyw, hzw);
536 m_geoManager->SetTopVolume(top);
537 m_volumes.push_back(top);
540 const auto nElements = m_cmp->GetNumberOfElements();
541 for (
size_t i = 0; i < nElements; ++i) {
543 bool driftmedium =
false;
544 std::vector<size_t> nodes;
545 if (!m_cmp->GetElement(i, mat, driftmedium, nodes))
continue;
547 if (driftmedium && !m_plotMeshBorders)
continue;
549 if (m_disabledMaterial.count(mat) > 0 && m_disabledMaterial[mat]) {
552 const short col = m_colorMap.count(mat) != 0 ? m_colorMap[mat] : 1;
555 const size_t nNodes = nodes.size();
556 if (nNodes != 4)
continue;
557 std::vector<std::array<double, 3> > v0;
558 for (
size_t j = 0; j < nNodes; ++j) {
559 double xn = 0., yn = 0., zn = 0.;
560 if (!m_cmp->GetNode(nodes[j], xn, yn, zn))
break;
561 v0.push_back({xn, yn, zn});
563 if (v0.size() != nNodes) {
565 <<
" Error retrieving nodes of element " << i <<
".\n";
569 std::array<std::array<double, 3>, 4> v;
571 for (
int nx = nMinX; nx <= nMaxX; nx++) {
572 const double dx = sx * nx;
574 if (m_cmp->m_mirrorPeriodic[0] && nx != 2 * (nx / 2)) {
575 for (
size_t j = 0; j < nNodes; ++j) {
576 v[j][0] = mapxmin + (mapxmax - v0[j][0]) + dx;
579 for (
size_t j = 0; j < nNodes; ++j) {
580 v[j][0] = v0[j][0] + dx;
585 for (
int ny = nMinY; ny <= nMaxY; ny++) {
586 const double dy = sy * ny;
588 if (m_cmp->m_mirrorPeriodic[1] && ny != 2 * (ny / 2)) {
589 for (
size_t j = 0; j < nNodes; ++j) {
590 v[j][1] = mapymin + (mapymax - v0[j][1]) + dy;
593 for (
size_t j = 0; j < nNodes; ++j) {
594 v[j][1] = v0[j][1] + dy;
599 for (
int nz = nMinZ; nz <= nMaxZ; nz++) {
600 const double dz = sz * nz;
602 if (m_cmp->m_mirrorPeriodic[2] && nz != 2 * (nz / 2)) {
603 for (
size_t j = 0; j < nNodes; ++j) {
604 v[j][2] = mapzmin + (mapzmax - v0[j][2]) + dz;
607 for (
size_t j = 0; j < nNodes; ++j) {
608 v[j][2] = v0[j][2] + dz;
611 auto tet =
new TGeoTet(
"Tet", v);
612 std::string vname =
"Tet" + std::to_string(m_volumes.size());
613 TGeoVolume* vol =
new TGeoVolume(vname.c_str(), tet, medDefault);
614 vol->SetLineColor(col);
615 vol->SetTransparency(70.);
616 m_volumes.push_back(vol);
617 top->AddNodeOverlap(vol, 1, gGeoIdentity);
622 m_geoManager->CloseGeometry();
623 top->SetTransparency(100.);
624 m_geoManager->SetTopVisible();
625 m_geoManager->SetMaxVisNodes(m_volumes.size() + 1);
626 m_geoManager->GetTopNode()->Draw(
"e");
629void ViewFEMesh::DrawDriftLines2d() {
631 if (!m_viewDrift)
return;
633 for (
const auto& driftLine : m_viewDrift->m_driftLines) {
636 gr.SetLineColor(m_viewDrift->m_colElectron);
638 gr.SetLineColor(m_viewDrift->m_colHole);
640 gr.SetLineColor(m_viewDrift->m_colIon);
642 std::vector<float> xgr;
643 std::vector<float> ygr;
645 for (
const auto& point : driftLine.first) {
647 float xp = 0., yp = 0.;
648 ToPlane(point[0], point[1], point[2], xp, yp);
650 if (InView(xp, yp)) {
656 gr.DrawGraph(xgr.size(), xgr.data(), ygr.data(),
"lsame");
662void ViewFEMesh::DrawDriftLines3d() {
664 if (!m_viewDrift)
return;
665 for (
const auto& driftLine : m_viewDrift->m_driftLines) {
666 std::vector<float> points;
667 for (
const auto& p : driftLine.first) {
668 points.push_back(p[0]);
669 points.push_back(p[1]);
670 points.push_back(p[2]);
672 const int nP = driftLine.first.size();
673 TPolyLine3D pl(nP, points.data());
675 pl.SetLineColor(m_viewDrift->m_colElectron);
677 pl.SetLineColor(m_viewDrift->m_colHole);
679 pl.SetLineColor(m_viewDrift->m_colIon);
682 pl.DrawPolyLine(nP, points.data(),
"same");
706 double mapxmax = m_cmp->m_mapmax[0];
707 double mapxmin = m_cmp->m_mapmin[0];
708 double mapymax = m_cmp->m_mapmax[1];
709 double mapymin = m_cmp->m_mapmin[1];
710 double mapzmax = m_cmp->m_mapmax[2];
711 double mapzmin = m_cmp->m_mapmin[2];
714 double sx = mapxmax - mapxmin;
715 double sy = mapymax - mapymin;
716 double sz = mapzmax - mapzmin;
717 const bool perX = m_cmp->m_periodic[0] || m_cmp->m_mirrorPeriodic[0];
718 const bool perY = m_cmp->m_periodic[1] || m_cmp->m_mirrorPeriodic[1];
719 const bool perZ = m_cmp->m_periodic[2] || m_cmp->m_mirrorPeriodic[2];
722 const int nMinX = perX ? int(
m_xMinBox / sx) - 1 : 0;
723 const int nMaxX = perX ? int(
m_xMaxBox / sx) + 1 : 0;
724 const int nMinY = perY ? int(
m_yMinBox / sy) - 1 : 0;
725 const int nMaxY = perY ? int(
m_yMaxBox / sy) + 1 : 0;
726 const int nMinZ = perZ ? int(
m_zMinBox / sz) - 1 : 0;
727 const int nMaxZ = perZ ? int(
m_zMaxBox / sz) + 1 : 0;
729 std::vector<PolygonInfo> elements;
730 int nMinU = 0, nMaxU = 0, nMinV = 0, nMaxV = 0;
731 double mapumin = 0., mapumax = 0., mapvmin = 0., mapvmax = 0.;
732 double su = 0., sv = 0.;
733 bool mirroru =
false, mirrorv =
false;
734 double uMin, vMin, uMax, vMax;
735 unsigned int n_x, n_y, n_z;
736 cst->GetNumberOfMeshLines(n_x, n_y, n_z);
741 constexpr double eps = 1.e-8;
742 if (
fabs(fx) < eps &&
fabs(fy) < eps &&
fabs(fz - 1.) < eps) {
744 std::cout <<
m_className <<
"::DrawCST: Creating x-y mesh view.\n";
746 unsigned int i, j,
z;
747 const double z0 =
m_plane[3] * fz;
748 if (!cst->Coordinate2Index(0, 0, z0, i, j, z)) {
749 std::cerr <<
" Could not determine the z-index of the plane.\n";
752 std::cout <<
" The z-index of the plane is " <<
z <<
".\n";
770 for (
unsigned int y = 0;
y < (n_y - 1);
y++) {
771 for (
unsigned int x = 0;
x < (n_x - 1);
x++) {
772 auto elem = cst->Index2Element(x, y, z);
773 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
774 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
776 PolygonInfo tmp_info;
777 tmp_info.element = elem;
778 tmp_info.p1[0] = e_xmin;
779 tmp_info.p2[0] = e_xmax;
780 tmp_info.p3[0] = e_xmax;
781 tmp_info.p4[0] = e_xmin;
782 tmp_info.p1[1] = e_ymin;
783 tmp_info.p2[1] = e_ymin;
784 tmp_info.p3[1] = e_ymax;
785 tmp_info.p4[1] = e_ymax;
786 elements.push_back(std::move(tmp_info));
789 }
else if (
fabs(fx) < eps &&
fabs(fy + 1.) < eps &&
fabs(fz) < eps) {
791 std::cout <<
m_className <<
"::DrawCST: Creating x-z mesh view.\n";
793 unsigned int i = 0, j = 0,
y = 0;
794 const double y0 =
m_plane[3] * fy;
795 if (!cst->Coordinate2Index(0, y0, 0, i, y, j)) {
796 std::cerr <<
" Could not determine the y-index of the plane.\n";
799 std::cout <<
" The y-index of the plane is " <<
y <<
".\n";
818 for (
unsigned int z = 0;
z < (n_z - 1);
z++) {
819 for (
unsigned int x = 0;
x < (n_x - 1);
x++) {
820 auto elem = cst->Index2Element(x, y, z);
821 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
822 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
824 PolygonInfo tmp_info;
825 tmp_info.element = elem;
826 tmp_info.p1[0] = e_xmin;
827 tmp_info.p2[0] = e_xmax;
828 tmp_info.p3[0] = e_xmax;
829 tmp_info.p4[0] = e_xmin;
830 tmp_info.p1[1] = e_zmin;
831 tmp_info.p2[1] = e_zmin;
832 tmp_info.p3[1] = e_zmax;
833 tmp_info.p4[1] = e_zmax;
834 elements.push_back(std::move(tmp_info));
837 }
else if (
fabs(fx + 1.) < eps &&
fabs(fy) < eps &&
fabs(fz) < eps) {
839 std::cout <<
m_className <<
"::DrawCST: Creating z-y mesh view.\n";
841 unsigned int i, j,
x;
842 const double x0 =
m_plane[3] * fx;
843 if (!cst->Coordinate2Index(x0, 0, 0, x, i, j)) {
844 std::cerr <<
" Could not determine the x-index of the plane.\n";
847 std::cout <<
" The x-index of the plane is " <<
x <<
".\n";
865 for (
unsigned int z = 0;
z < (n_z - 1);
z++) {
866 for (
unsigned int y = 0;
y < (n_y - 1);
y++) {
867 auto elem = cst->Index2Element(x, y, z);
868 double e_xmin, e_xmax, e_ymin, e_ymax, e_zmin, e_zmax;
869 cst->GetElementBoundaries(elem, e_xmin, e_xmax, e_ymin, e_ymax,
871 PolygonInfo tmp_info;
872 tmp_info.element = elem;
873 tmp_info.p1[0] = e_zmin;
874 tmp_info.p2[0] = e_zmax;
875 tmp_info.p3[0] = e_zmax;
876 tmp_info.p4[0] = e_zmin;
877 tmp_info.p1[1] = e_ymin;
878 tmp_info.p2[1] = e_ymin;
879 tmp_info.p3[1] = e_ymax;
880 tmp_info.p4[1] = e_ymax;
882 elements.push_back(std::move(tmp_info));
887 std::cerr <<
" The given plane name is not known.\n";
888 std::cerr <<
" Please choose one of the following: xy, xz, yz.\n";
892 std::cout <<
m_className <<
"::DrawCST:\n " << elements.size()
893 <<
" elements in the projection of the unit cell.\n";
894 for (
const auto& element : elements) {
896 bool driftmedium =
false;
897 std::vector<size_t> nodes;
898 cst->GetElement(element.element, mat, driftmedium, nodes);
900 if (driftmedium && !m_plotMeshBorders)
continue;
902 if (m_disabledMaterial[mat])
continue;
904 const short col = m_colorMap.count(mat) > 0 ? m_colorMap[mat] : 1;
905 gr.SetLineColor(col);
906 if (m_colorMap_fill.count(mat) > 0) {
907 gr.SetFillColor(m_colorMap_fill[mat]);
909 gr.SetFillColor(col);
911 if (m_plotMeshBorders)
916 for (
int nu = nMinU; nu <= nMaxU; nu++) {
917 for (
int nv = nMinV; nv <= nMaxV; nv++) {
919 float tmp_u[4], tmp_v[4];
920 if (mirroru && nu != 2 * (nu / 2)) {
922 tmp_u[0] = mapumin + (mapumax - element.p1[0]) + su * nu;
923 tmp_u[1] = mapumin + (mapumax - element.p2[0]) + su * nu;
924 tmp_u[2] = mapumin + (mapumax - element.p3[0]) + su * nu;
925 tmp_u[3] = mapumin + (mapumax - element.p4[0]) + su * nu;
928 tmp_u[0] = element.p1[0] + su * nu;
929 tmp_u[1] = element.p2[0] + su * nu;
930 tmp_u[2] = element.p3[0] + su * nu;
931 tmp_u[3] = element.p4[0] + su * nu;
933 if (mirrorv && nv != 2 * (nv / 2)) {
934 tmp_v[0] = mapvmin + (mapvmax - element.p1[1]) + sv * nv;
935 tmp_v[1] = mapvmin + (mapvmax - element.p2[1]) + sv * nv;
936 tmp_v[2] = mapvmin + (mapvmax - element.p3[1]) + sv * nv;
937 tmp_v[3] = mapvmin + (mapvmax - element.p4[1]) + sv * nv;
939 tmp_v[0] = element.p1[1] + sv * nv;
940 tmp_v[1] = element.p2[1] + sv * nv;
941 tmp_v[2] = element.p3[1] + sv * nv;
942 tmp_v[3] = element.p4[1] + sv * nv;
944 if (tmp_u[0] < uMin || tmp_u[1] > uMax || tmp_v[0] < vMin ||
948 std::string opt =
"";
949 if (m_plotMeshBorders || !m_fillMesh) opt +=
"l";
950 if (m_fillMesh) opt +=
"f";
952 gr.DrawGraph(4, tmp_u, tmp_v, opt.c_str());
967void ViewFEMesh::RemoveCrossings(std::vector<double>& x,
968 std::vector<double>& y) {
970 double xmin =
x[0], xmax =
x[0];
971 double ymin =
y[0], ymax =
y[0];
972 for (
int i = 1; i < (int)
x.size(); i++) {
973 if (x[i] < xmin) xmin =
x[i];
974 if (x[i] > xmax) xmax =
x[i];
975 if (y[i] < ymin) ymin =
y[i];
976 if (y[i] > ymax) ymax =
y[i];
980 double xtol = 1e-10 * std::abs(xmax - xmin);
981 double ytol = 1e-10 * std::abs(ymax - ymin);
982 for (
int i = 0; i < (int)
x.size(); i++) {
983 for (
int j = i + 1; j < (int)
x.size(); j++) {
984 if (std::abs(x[i] - x[j]) < xtol && std::abs(y[i] - y[j]) < ytol) {
985 x.erase(
x.begin() + j);
986 y.erase(
y.begin() + j);
993 if (
x.size() <= 3)
return;
1003 bool crossings =
true;
1004 while (crossings && (attempts < NN)) {
1008 for (
int i = 1; i <= NN; i++) {
1009 for (
int j = i + 2; j <= NN; j++) {
1011 if ((j + 1) > NN && 1 + (j % NN) >= i)
break;
1014 double xc = 0., yc = 0.;
1015 if (!LinesCrossed(x[(i - 1) % NN], y[(i - 1) % NN], x[i % NN],
1016 y[i % NN], x[(j - 1) % NN], y[(j - 1) % NN],
1017 x[j % NN], y[j % NN], xc, yc)) {
1022 for (
int k = 1; k <= (j - i) / 2; k++) {
1023 double xs =
x[(i + k - 1) % NN];
1024 double ys =
y[(i + k - 1) % NN];
1025 x[(i + k - 1) % NN] = x[(j - k) % NN];
1026 y[(i + k - 1) % NN] = y[(j - k) % NN];
1027 x[(j - k) % NN] = xs;
1028 y[(j - k) % NN] = ys;
1041 if (attempts > NN) {
1042 std::cerr <<
m_className <<
"::RemoveCrossings:\n Warning: "
1043 <<
"Maximum attempts reached. Crossings not removed.\n";
1048bool ViewFEMesh::InView(
const double x,
const double y)
const {
1055 return IsInPolygon(x, y, m_viewRegionX, m_viewRegionY, edge);
1065bool ViewFEMesh::LinesCrossed(
double x1,
double y1,
double x2,
double y2,
1066 double u1,
double v1,
double u2,
double v2,
1067 double& xc,
double& yc)
const {
1069 double xtol = 1.0e-10 * std::max({std::abs(x1), std::abs(x2), std::abs(u1),
1071 double ytol = 1.0e-10 * std::max({std::abs(y1), std::abs(y2), std::abs(v1),
1073 if (xtol <= 0) xtol = 1.0e-10;
1074 if (ytol <= 0) ytol = 1.0e-10;
1077 double dy = y2 - y1;
1078 double dv = v2 - v1;
1079 double dx = x1 - x2;
1080 double du = u1 - u2;
1081 double det = dy * du - dx * dv;
1084 if (OnLine(x1, y1, x2, y2, u1, v1)) {
1088 }
else if (OnLine(x1, y1, x2, y2, u2, v2)) {
1092 }
else if (OnLine(u1, v1, u2, v2, x1, y1)) {
1096 }
else if (OnLine(u1, v1, u2, v2, x2, y2)) {
1102 if (std::abs(det) < xtol * ytol)
return false;
1106 xc = (du * (x1 * y2 - x2 * y1) - dx * (u1 * v2 - u2 * v1)) / det;
1107 yc = ((-1 * dv) * (x1 * y2 - x2 * y1) + dy * (u1 * v2 - u2 * v1)) / det;
1110 if (OnLine(x1, y1, x2, y2, xc, yc) && OnLine(u1, v1, u2, v2, xc, yc))
1122bool ViewFEMesh::OnLine(
double x1,
double y1,
double x2,
double y2,
double u,
1125 double xtol = 1.e-10 * std::max({std::abs(x1), std::abs(x2), std::abs(u)});
1126 double ytol = 1.e-10 * std::max({std::abs(y1), std::abs(y2), std::abs(v)});
1127 if (xtol <= 0) xtol = 1.0e-10;
1128 if (ytol <= 0) ytol = 1.0e-10;
1131 double xc = 0, yc = 0;
1134 if ((std::abs(x1 - u) <= xtol && std::abs(y1 - v) <= ytol) ||
1135 (std::abs(x2 - u) <= xtol && std::abs(y2 - v) <= ytol)) {
1139 if (std::abs(x1 - x2) <= xtol && std::abs(y1 - y2) <= ytol) {
1143 if (std::abs(u - x1) + std::abs(v - y1) <
1144 std::abs(u - x2) + std::abs(v - y2)) {
1147 double dpar = ((u - x1) * (x2 - x1) + (v - y1) * (y2 - y1)) /
1148 ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
1154 }
else if (dpar > 1.0) {
1158 xc = x1 + dpar * (x2 - x1);
1159 yc = y1 + dpar * (y2 - y1);
1165 double dpar = ((u - x2) * (x1 - x2) + (v - y2) * (y1 - y2)) /
1166 ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
1172 }
else if (dpar > 1.0) {
1176 xc = x2 + dpar * (x1 - x2);
1177 yc = y2 + dpar * (y1 - y2);
1182 if (std::abs(u - xc) < xtol && std::abs(v - yc) < ytol)
return true;
1192bool ViewFEMesh::PlaneCut(
double x1,
double y1,
double z1,
double x2,
double y2,
1193 double z2, TMatrixD& xMat) {
1196 TMatrixD cutMat(3, 3);
1197 dataCut[0] =
m_proj[0][0];
1198 dataCut[1] =
m_proj[1][0];
1199 dataCut[2] = x1 - x2;
1200 dataCut[3] =
m_proj[0][1];
1201 dataCut[4] =
m_proj[1][1];
1202 dataCut[5] = y1 - y2;
1203 dataCut[6] =
m_proj[0][2];
1204 dataCut[7] =
m_proj[1][2];
1205 dataCut[8] = z1 - z2;
1206 cutMat.SetMatrixArray(dataCut.GetArray());
1211 (cutMat(1, 1) * cutMat(2, 2) - cutMat(1, 2) * cutMat(2, 1)) -
1213 (cutMat(1, 0) * cutMat(2, 2) - cutMat(1, 2) * cutMat(2, 0)) +
1215 (cutMat(1, 0) * cutMat(2, 1) - cutMat(1, 1) * cutMat(2, 0));
1218 if (std::abs(cutDet) < 1e-20)
return false;
1221 TArrayD dataCoords(3);
1222 TMatrixD coordMat(3, 1);
1223 dataCoords[0] = x1 -
m_proj[2][0];
1224 dataCoords[1] = y1 -
m_proj[2][1];
1225 dataCoords[2] = z1 -
m_proj[2][2];
1226 coordMat.SetMatrixArray(dataCoords.GetArray());
1229 cutMat.SetTol(1e-20);
1232 if (!cutMat.IsValid())
return false;
1233 xMat = cutMat * coordMat;
1236 if (xMat(2, 0) < 0 || xMat(2, 0) > 1)
return false;
1242bool ViewFEMesh::IntersectPlaneArea(
double& xmin,
double& ymin,
1243 double& xmax,
double& ymax) {
1244 std::vector<TMatrixD> intersect_points;
1245 m_viewRegionX.clear();
1246 m_viewRegionY.clear();
1248 for (
int i0 = 0; i0 < 2; ++i0) {
1249 for (
int j0 = 0; j0 < 2; ++j0) {
1250 for (
int k0 = 0; k0 < 2; ++k0) {
1251 for (
int i1 = i0; i1 < 2; ++i1) {
1252 for (
int j1 = j0; j1 < 2; ++j1) {
1253 for (
int k1 = k0; k1 < 2; ++k1) {
1254 if (i1 - i0 + j1 - j0 + k1 - k0 != 1)
continue;
1261 TMatrixD xMat(3, 1);
1262 if (!PlaneCut(x0, y0, z0, x1, y1, z1, xMat))
continue;
1264 std::cout <<
m_className <<
"::IntersectPlaneArea:\n"
1265 <<
" Intersection of plane at (" << xMat(0, 0)
1266 <<
", " << xMat(1, 0) <<
", " << xMat(2, 0)
1267 <<
") with edge\n ("
1268 << x0 <<
", " << y0 <<
", " << z0 <<
")-("
1269 << x1 <<
", " << y1 <<
", " << z1 <<
")\n";
1273 for (
auto& p : intersect_points) {
1274 const double dx = xMat(0, 0) - p(0, 0);
1275 const double dy = xMat(1, 0) - p(1, 0);
1276 if (std::hypot(dx, dy) < 1e-10) {
1281 if (!skip) intersect_points.push_back(xMat);
1288 if (intersect_points.size() < 3) {
1289 std::cerr <<
m_className <<
"::IntersectPlaneArea:\n"
1290 <<
" WARNING: Empty intersection of view plane with area.\n";
1293 TMatrixD offset = intersect_points[0];
1294 xmin = xmax = intersect_points[0](0, 0);
1295 ymin = ymax = intersect_points[0](1, 0);
1297 for (
auto& p : intersect_points) p -= offset;
1298 std::sort(intersect_points.begin(), intersect_points.end(),
1299 [](
const TMatrixD& a,
const TMatrixD& b) ->
bool {
1300 double cross_z = a(0, 0) * b(1, 0) - a(1, 0) * b(0, 0);
1303 for (
auto& p : intersect_points) {
1305 m_viewRegionX.push_back(p(0, 0));
1306 m_viewRegionY.push_back(p(1, 0));
1307 xmin = std::min(p(0, 0), xmin);
1308 ymin = std::min(p(1, 0), ymin);
1309 xmax = std::max(p(0, 0), xmax);
1310 ymax = std::max(p(1, 0), ymax);
1322bool ViewFEMesh::IsInPolygon(
double x,
double y,
1323 const std::vector<double>& px,
1324 const std::vector<double>& py,
bool& edge)
const {
1326 const size_t pN = px.size();
1329 if (pN < 2)
return false;
1331 if (pN == 2)
return OnLine(px[0], py[0], px[1], py[1], x, y);
1334 double px_min = px[0], py_min = py[0];
1335 double px_max = px[0], py_max = py[0];
1336 for (
size_t i = 0; i < pN; i++) {
1337 px_min = std::min(px_min, px[i]);
1338 py_min = std::min(py_min, py[i]);
1339 px_max = std::max(px_max, px[i]);
1340 py_max = std::max(py_max, py[i]);
1344 double xtol = 1.0e-10 * std::max(std::abs(px_min), std::abs(px_max));
1345 double ytol = 1.0e-10 * std::max(std::abs(py_min), std::abs(py_max));
1346 if (xtol <= 0) xtol = 1.0e-10;
1347 if (ytol <= 0) ytol = 1.0e-10;
1350 if (std::abs(px_max - px_min) < xtol) {
1351 edge = (
y > (py_min - ytol) &&
y < (py_max + ytol) &&
1352 std::abs(px_max + px_min - 2 * x) < xtol);
1356 if (std::abs(py_max - py_min) < ytol) {
1357 edge = (
x > (px_min - xtol) &&
x < (px_max + xtol) &&
1358 std::abs(py_max + py_min - 2 * y) < ytol);
1363 double xinf = px_min - std::abs(px_max - px_min);
1364 double yinf = py_min - std::abs(py_max - py_min);
1371 while (!done && niter < 100) {
1378 for (
size_t i = 0; (done && i < pN); i++) {
1380 if (OnLine(px[i % pN], py[i % pN], px[(i + 1) % pN], py[(i + 1) % pN], x,
1387 double xc = 0., yc = 0.;
1388 if (LinesCrossed(x, y, xinf, yinf, px[i % pN], py[i % pN],
1389 px[(i + 1) % pN], py[(i + 1) % pN], xc, yc))
1394 if (OnLine(x, y, xinf, yinf, px[i], py[i])) {
1396 xinf = px_min -
RndmUniform() * std::abs(px_max - xinf);
1397 yinf = py_min -
RndmUniform() * std::abs(py_max - yinf);
1408 std::cerr <<
m_className <<
"::IsInPolygon: Unable to determine whether ("
1409 <<
x <<
", " <<
y <<
") is inside a polygon. Returning false.\n";
1414 return (ncross != 2 * (ncross / 2));
1423void ViewFEMesh::ClipToView(std::vector<double>& px, std::vector<double>& py,
1424 std::vector<double>& cx, std::vector<double>& cy) {
1426 int pN = (int)px.size();
1433 const auto& vx = m_viewRegionX;
1434 const auto& vy = m_viewRegionY;
1435 const int vN = m_viewRegionX.size();
1441 for (
int i = 0; i < pN; i++) {
1446 for (
int j = 0; j < vN; j++) {
1449 if (OnLine(vx[j % vN], vy[j % vN], vx[(j + 1) % vN], vy[(j + 1) % vN],
1452 cx.push_back(px[i]);
1453 cy.push_back(py[i]);
1461 if (OnLine(px[i % pN], py[i % pN], px[(i + 1) % pN], py[(i + 1) % pN],
1464 cx.push_back(vx[j]);
1465 cy.push_back(vy[j]);
1476 for (
int j = 0; j < vN; j++) {
1479 double xc = 0., yc = 0.;
1480 if (LinesCrossed(vx[j % vN], vy[j % vN], vx[(j + 1) % vN],
1481 vy[(j + 1) % vN], px[i % pN], py[i % pN],
1482 px[(i + 1) % pN], py[(i + 1) % pN], xc, yc)) {
1491 for (
int j = 0; j < vN; j++) {
1495 if (IsInPolygon(vx[j], vy[j], px, py, edge)) {
1497 cx.push_back(vx[j]);
1498 cy.push_back(vy[j]);
1503 for (
int i = 0; i < pN; i++) {
1507 if (IsInPolygon(px[i], py[i], vx, vy, edge)) {
1509 cx.push_back(px[i]);
1510 cy.push_back(py[i]);
Base class for components based on finite-element field maps.
std::array< double, 3 > m_mapmin
virtual size_t GetNumberOfElements() const
Return the number of mesh elements.
bool GetElement(const size_t i, double &vol, double &dmin, double &dmax) const
Return the volume and aspect ratio of a mesh element.
std::array< double, 3 > m_mapmax
std::array< bool, 3 > m_mirrorPeriodic
Mirror periodicity in x, y, z.
std::array< bool, 3 > m_periodic
Simple periodicity in x, y, z.
std::array< double, 4 > m_plane
static void SetRange(TVirtualPad *pad, const double x0, const double y0, const double x1, const double y1)
std::array< std::array< double, 3 >, 3 > m_proj
TPad * GetCanvas()
Retrieve the canvas.
static bool RangeSet(TVirtualPad *)
void ToPlane(const T x, const T y, const T z, T &xp, T &yp) const
ViewBase()=delete
Default constructor.
virtual void SetPlane(const double fx, const double fy, const double fz, const double x0, const double y0, const double z0)
bool Plot(const bool twod=true)
Plot method to be called by user.
void CreateDefaultAxes()
Create a default set of custom-made axes.
void SetPlane(const double fx, const double fy, const double fz, const double x0, const double y0, const double z0) override
void SetComponent(ComponentFieldMap *cmp)
Set the component from which to retrieve the mesh and field.
void SetXaxis(TGaxis *ax)
void SetYaxis(TGaxis *ay)
double RndmUniform()
Draw a random number uniformly distributed in the range [0, 1).
DoubleAc fabs(const DoubleAc &f)