Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
SurfaceMeshActor.cpp
Go to the documentation of this file.
1/* This file is part of MoDELib, distributed under GNU GPL v2. */
3#include <SurfaceMeshActor.h>
4#include <GlidePlaneFactory.h>
5#include <QGridLayout>
6#include <QSignalBlocker>
7#include <sstream>
8#include <exception>
9#include <cstddef>
10#include <vtkCellArray.h>
11#include <vtkPoints.h>
12#include <vtkProperty.h>
13#include <vtkLightCollection.h>
14
15#include <DDqtControls.h>
16
17namespace model
18{
19 SurfaceMeshActor::SurfaceMeshActor(vtkGenericOpenGLRenderWindow *window, vtkRenderer *rendererIn,
20 const DefectiveCrystal<3> &crystal)
21 : renderWindow(window), renderer(rendererIn), defectiveCrystal(crystal),
22 showSurface(new QCheckBox("Show deformed surface", this)), meshSizeEdit(new QLineEdit(this)),
23 amplification(new QDoubleSpinBox(this)), showEdges(new QCheckBox("Show triangle edges", this)),
24 status(new QLabel(this)), clipSurface(new QCheckBox("Clip surface", this)),
25 clipCenterEdit(new QLineEdit(this)), clipNormalEdit(new QLineEdit("1 0 0", this)),
26 clipStatus(new QLabel(this)), clipPositionSlider(new QSlider(Qt::Horizontal, this)),
27 clipOffsetLabel(new QLabel("Offset: 0 b", this)),
28 externalFaces(new controls::GroupBox("External Faces", this)),
29 internalFaces(new controls::GroupBox("Internal Faces", this)), polyData(vtkSmartPointer<vtkPolyData>::New()),
30 clipPlane(vtkSmartPointer<vtkPlane>::New()), clipper(vtkSmartPointer<vtkClipPolyData>::New()),
31 shadingMesh(vtkSmartPointer<vtkCleanPolyData>::New()),
32 normals(vtkSmartPointer<vtkPolyDataNormals>::New()), mapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
33 actor(vtkSmartPointer<vtkActor>::New()), reliefLight(vtkSmartPointer<vtkLight>::New())
34 {
35 meshSizeEdit->setObjectName("surfaceMeshArea");
36 amplification->setObjectName("surfaceDisplacementAmplification");
37 showSurface->setObjectName("showSurfaceMesh");
38 const double area =
39 std::max(1.0, (crystal.ddBase.mesh.xMax() - crystal.ddBase.mesh.xMin()).squaredNorm() / 10000.0);
40 meshSizeEdit->setText(QString::number(area, 'g', 8));
41 meshSizeEdit->setToolTip(
42 "Maximum reference triangle area in b², as in the Fields tab. Smaller values resolve finer steps.");
43 amplification->setRange(0.0, 1.e6);
44 amplification->setDecimals(3);
45 amplification->setValue(1.0);
46 amplification->setToolTip("Displayed position = reference position + amplification × displacement. Zero shows "
47 "the reference surface.");
48 auto *layout = new QGridLayout(this);
49 auto *surfaceBox = controls::box(
50 "Surface", this, "Show the displaced selected mesh faces, including sharp slip-step walls.", showSurface);
51 auto *surfaceLayout = controls::grid(surfaceBox);
52 layout->addWidget(surfaceBox, 0, 0);
53 controls::field(surfaceLayout, 0, "Mesh area (b²)", meshSizeEdit,
54 "Maximum reference triangle area in b squared. Smaller values give finer detail and more "
55 "computation. Press Enter to rebuild.");
56 controls::field(surfaceLayout, 1, "Amplification", amplification,
57 "Displayed position equals reference position plus this factor times displacement. Zero shows "
58 "the reference shape; one is physical scale.");
59 surfaceLayout->addWidget(showEdges, 2, 0, 1, 2);
60 showEdges->setToolTip("Draw triangle edges over the shaded surface to inspect the mesh resolution.");
61 auto *physicsBox =
62 controls::box("Physics", this, "Select which simulated microstructures contribute to displacement.");
63 surfaceLayout->addWidget(physicsBox, 3, 0, 1, 2);
64 int row = 0;
65 for(const auto &microstructure : defectiveCrystal.microstructures())
66 {
67 auto *check = new QCheckBox(QString::fromStdString(microstructure->tag), this);
68 check->setChecked(true);
69 microstructuresCheck.push_back(check);
70 controls::grid(physicsBox)->addWidget(check, row++, 0, 1, 2);
71 check->setToolTip("Include this physics contribution in the surface displacement. Changes recompute the "
72 "visible surface.");
73 connect(check, &QCheckBox::toggled, this,
74 [this](bool)
75 {
76 dirty = true;
77 validDisplacement = false;
78 if(frameAvailable && showSurface->isChecked())
79 {
80 compute();
81 }
82 render();
83 });
84 }
85 const Eigen::Vector3d center = 0.5 * (crystal.ddBase.mesh.xMin() + crystal.ddBase.mesh.xMax());
86 clipCenterEdit->setText(
87 QString("%1 %2 %3").arg(center.x(), 0, 'g', 16).arg(center.y(), 0, 'g', 16).arg(center.z(), 0, 'g', 16));
88 clipCenterEdit->setObjectName("surfaceClipCenter");
89 clipNormalEdit->setObjectName("surfaceClipNormal");
90 clipSurface->setObjectName("clipSurfaceMesh");
91 clipCenterEdit->setToolTip("Global x y z in b. Press Enter to apply.");
92 clipNormalEdit->setToolTip("Global nx ny nz. Keeps the side toward the normal; reverse it to keep the other "
93 "side. Press Enter to apply.");
94 auto *clipBox = controls::box(
95 "Clipping", this, "Trim the displaced surface with a plane. The side toward the normal is retained.",
97 surfaceLayout->addWidget(clipBox, 5, 0, 1, 2);
98 auto *clipLayout = controls::grid(clipBox);
99 controls::field(clipLayout, 0, "Center (b)", clipCenterEdit,
100 "Global x y z coordinates of the reference clip-plane center, in b. Press Enter to apply and "
101 "reset the offset.");
102 controls::field(clipLayout, 1, "Normal", clipNormalEdit,
103 "Global nx ny nz components of a nonzero normal. It is normalized automatically; reverse it to "
104 "keep the other half-space. Press Enter to apply.");
105 row = 2;
106 appliedClipCenter = center;
107 clipTravel = (crystal.ddBase.mesh.xMax() - crystal.ddBase.mesh.xMin()).norm();
108 clipPositionSlider->setObjectName("surfaceClipPosition");
109 clipPositionSlider->setRange(-1000, 1000);
110 clipPositionSlider->setValue(0);
111 clipPositionSlider->setPageStep(100);
112 clipPositionSlider->setEnabled(false);
113 clipPositionSlider->setToolTip("Move along the applied normal. Zero returns to the entered center; range is "
114 "one box diagonal in each direction.");
115 clipLayout->addWidget(clipOffsetLabel, row++, 0, 1, 2);
116 clipLayout->addWidget(clipPositionSlider, row++, 0, 1, 2);
117 connect(clipPositionSlider, &QSlider::valueChanged, this, &SurfaceMeshActor::moveClipPlane);
118 clipStatus->setWordWrap(true);
119 clipLayout->addWidget(clipStatus, row++, 0, 1, 2);
120 clipPlane->SetOrigin(center.data());
121 clipPlane->SetNormal(1.0, 0.0, 0.0);
122 clipper->SetInputData(polyData);
123 clipper->SetClipFunction(clipPlane);
124 clipper->SetValue(0.0);
125 clipper->InsideOutOff();
126 connect(clipSurface, &QCheckBox::toggled, this, &SurfaceMeshActor::updateClipping);
127 connect(clipCenterEdit, &QLineEdit::returnPressed, this, &SurfaceMeshActor::updateClipping);
128 connect(clipNormalEdit, &QLineEdit::returnPressed, this, &SurfaceMeshActor::updateClipping);
129 externalFaces->setObjectName("surfaceExternalFaces");
130 internalFaces->setObjectName("surfaceInternalFaces");
131 externalFaces->setCheckable(true);
132 internalFaces->setCheckable(true);
133 externalFaces->setChecked(false);
134 internalFaces->setChecked(false);
135 externalFaces->setToolTip("The first activation selects all external faces. Refine individual selections; disabling and re-enabling this group preserves them.");
136 internalFaces->setToolTip("The first activation selects all internal grain interfaces, once per face. Refine individual selections; disabling and re-enabling this group preserves them.");
137 auto* externalLayout=new QGridLayout(externalFaces);
138 auto* internalLayout=new QGridLayout(internalFaces);
139 for(const auto& region : crystal.ddBase.mesh.regions())
140 {
141 for(const auto& face : region.second->faces())
142 {
143 const size_t id=face.second->sID;
144 const bool external=face.second->isExternal();
145 auto& checks=external?externalFaceChecks:internalFaceChecks;
146 auto* group=external?externalFaces:internalFaces;
147 if(!checks.count(id))
148 {
149 auto* check=new QCheckBox(QString("Face %1").arg(id),group);
150 check->setObjectName(QString(external?"surfaceExternalFace_%1":"surfaceInternalFace_%1").arg(id));
151 check->setChecked(false);
152 check->setToolTip(QString("Include mesh face %1, its displacement field and slip-step walls. Face IDs use the mesh numbering.").arg(id));
153 checks.emplace(id,check);
154 }
155 }
156 }
157 const auto populate=[this](const auto& checks,QGridLayout* grid)
158 {
159 int index=0;
160 for(const auto& entry : checks)
161 {
162 grid->addWidget(entry.second,index/2,index%2);
163 ++index;
164 connect(entry.second,&QCheckBox::toggled,this,&SurfaceMeshActor::rebuild);
165 }
166 };
167 populate(externalFaceChecks,externalLayout);
168 populate(internalFaceChecks,internalLayout);
169 surfaceLayout->addWidget(externalFaces,6,0,1,2);
170 surfaceLayout->addWidget(internalFaces,7,0,1,2);
171 const auto connectFaceGroup=[this](auto* group,const auto* checks)
172 {
173 connect(group,&QGroupBox::toggled,this,[this,checks,firstEnable=true](bool enabled) mutable
174 {
175 if(enabled && firstEnable)
176 {
177 for(const auto& entry : *checks)
178 {
179 const QSignalBlocker blocker(entry.second);
180 entry.second->setChecked(true);
181 }
182 firstEnable=false;
183 }
184 rebuild();
185 });
186 };
187 connectFaceGroup(externalFaces,&externalFaceChecks);
188 connectFaceGroup(internalFaces,&internalFaceChecks);
189 status->setWordWrap(true);
190 layout->addWidget(status, 1, 0);
191 layout->setRowStretch(2, 1);
192 shadingMesh->SetInputData(polyData);
193 // Slip-trace cutting duplicates polygon vertices. Weld only exactly
194 // coincident display points so smooth regions share point normals;
195 // never use a distance tolerance that could close a small slip step.
196 shadingMesh->SetTolerance(0.0);
197 shadingMesh->PointMergingOn();
198 shadingMesh->ConvertPolysToLinesOff();
199 shadingMesh->ConvertLinesToPointsOff();
200 shadingMesh->ConvertStripsToPolysOff();
201 normals->SetInputConnection(shadingMesh->GetOutputPort());
202 normals->ComputePointNormalsOn();
203 normals->SplittingOn();
204 normals->SetFeatureAngle(30.0);
205 normals->ComputeCellNormalsOn();
206 normals->ConsistencyOn();
207 normals->AutoOrientNormalsOff();
208 mapper->SetInputConnection(normals->GetOutputPort());
209 mapper->ScalarVisibilityOff();
210 actor->SetMapper(mapper);
211 actor->GetProperty()->SetColor(0.65, 0.65, 0.65);
212 actor->GetProperty()->SetAmbient(0.25);
213 actor->GetProperty()->SetDiffuse(0.75);
214 actor->GetProperty()->SetSpecular(0.0);
215 auto *shadingBox = controls::box("Shading", this, "Adjust the surface material lighting without recomputing displacement.");
216 surfaceLayout->addWidget(shadingBox, 4, 0, 1, 2);
217 auto *ambient = new QDoubleSpinBox(shadingBox);
218 auto *diffuse = new QDoubleSpinBox(shadingBox);
219 auto *specular = new QDoubleSpinBox(shadingBox);
220 ambient->setObjectName("surfaceAmbient");
221 diffuse->setObjectName("surfaceDiffuse");
222 specular->setObjectName("surfaceSpecular");
223 for(auto *coefficient : {ambient, diffuse, specular})
224 {
225 coefficient->setRange(0.0, 1.0);
226 coefficient->setDecimals(2);
227 coefficient->setSingleStep(0.05);
228 }
229 ambient->setValue(actor->GetProperty()->GetAmbient());
230 diffuse->setValue(actor->GetProperty()->GetDiffuse());
231 specular->setValue(actor->GetProperty()->GetSpecular());
232 controls::field(controls::grid(shadingBox), 0, "Ambient", ambient,
233 "Uniform illumination from 0 to 1. Increase to soften dark regions and reduce orientation contrast. Changes apply immediately.");
234 controls::field(controls::grid(shadingBox), 1, "Diffuse", diffuse,
235 "Directional matte lighting from 0 to 1. Higher values emphasize surface slope relative to the light. Changes apply immediately.");
236 controls::field(controls::grid(shadingBox), 2, "Specular", specular,
237 "Reflective highlight strength from 0 to 1. Zero gives a matte surface; higher values emphasize highlights. Changes apply immediately.");
238 const auto updateShading = [this, ambient, diffuse, specular]()
239 {
240 actor->GetProperty()->SetAmbient(ambient->value());
241 actor->GetProperty()->SetDiffuse(diffuse->value());
242 actor->GetProperty()->SetSpecular(specular->value());
243 render();
244 };
245 for(auto *coefficient : {ambient, diffuse, specular})
246 {
247 connect(coefficient, &QDoubleSpinBox::valueChanged, this, updateShading);
248 }
249 actor->GetProperty()->SetInterpolationToPhong();
250 actor->GetProperty()->SetEdgeColor(0.15, 0.15, 0.15);
251 actor->SetVisibility(false);
252 renderer->AddActor(actor);
253 // An oblique camera-relative light reveals relief as the view rotates.
254 // Add it only while visible so hidden surfaces do not suppress VTK's
255 // automatic default light or change the lighting of the other actors.
256 reliefLight->SetLightTypeToCameraLight();
257 reliefLight->SetPosition(-1.0, 1.0, 0.8);
258 reliefLight->SetFocalPoint(0.0, 0.0, 0.0);
259 reliefLight->PositionalOff();
260 reliefLight->SetIntensity(0.8);
261 status->setText("Enable the surface after loading a frame.");
262 connect(meshSizeEdit, &QLineEdit::returnPressed, this, &SurfaceMeshActor::rebuild);
263 connect(showSurface, &QCheckBox::toggled, this,
264 [this](bool enabled)
265 {
266 if(enabled && frameAvailable)
267 {
268 compute();
269 }
270 actor->SetVisibility(enabled && validDisplacement);
271 render();
272 });
273 connect(showEdges, &QCheckBox::toggled, this,
274 [this](bool enabled)
275 {
276 actor->GetProperty()->SetEdgeVisibility(enabled);
277 render();
278 });
279 connect(amplification, &QDoubleSpinBox::valueChanged, this,
280 [this](double)
281 {
282 if(validDisplacement)
283 {
284 try
285 {
286 applyDisplacement();
287 }
288 catch(const std::exception &error)
289 {
290 reportError(error);
291 }
292 }
293 render();
294 });
295 }
296
298 {
299 renderer->RemoveLight(reliefLight);
300 renderer->RemoveActor(actor);
301 }
302
303 void SurfaceMeshActor::updateLighting()
304 {
305 const bool attached = renderer->GetLights()->IsItemPresent(reliefLight) != 0;
306 if(actor->GetVisibility() && !attached)
307 {
308 renderer->AddLight(reliefLight);
309 }
310 else if(!actor->GetVisibility() && attached)
311 {
312 renderer->RemoveLight(reliefLight);
313 }
314 }
315
316 void SurfaceMeshActor::render()
317 {
318 updateLighting();
319 if(renderWindow->GetReadyForRendering())
320 {
321 renderWindow->Render();
322 }
323 }
324
325 void SurfaceMeshActor::moveClipPlane()
326 {
327 const double offset = clipTravel * clipPositionSlider->value() / 1000.0;
328 const Eigen::Vector3d center = appliedClipCenter + offset * appliedClipNormal;
329 clipPlane->SetOrigin(center.data());
330 clipOffsetLabel->setText(QString("Offset: %1 b").arg(offset, 0, 'g', 6));
331 if(validDisplacement)
332 {
333 normals->Update();
334 renderer->ResetCameraClippingRange();
335 }
336 render();
337 }
338
340 {
341 try
342 {
343 if(clipSurface->isChecked())
344 {
345 const auto readVector = [](const QLineEdit *edit) -> Eigen::Vector3d
346 {
347 std::istringstream stream(edit->text().toStdString());
348 Eigen::Vector3d value;
349 std::string extra;
350 if(!(stream >> value.x() >> value.y() >> value.z()) || (stream >> extra) || !value.allFinite())
351 {
352 throw std::invalid_argument("Enter three finite numbers for each clip-plane vector.");
353 }
354 return value;
355 };
356 const Eigen::Vector3d center = readVector(clipCenterEdit);
357 Eigen::Vector3d normal = readVector(clipNormalEdit);
358 const double scale = normal.cwiseAbs().maxCoeff();
359 if(scale == 0.0)
360 {
361 throw std::invalid_argument("Clip-plane normal must be nonzero.");
362 }
363 normal /= scale;
364 normal.normalize();
365 if(center != appliedClipCenter || normal != appliedClipNormal)
366 {
367 const QSignalBlocker blocker(clipPositionSlider);
368 clipPositionSlider->setValue(0);
369 }
370 appliedClipCenter = center;
371 appliedClipNormal = normal;
372 clipPlane->SetNormal(normal.data());
373 shadingMesh->SetInputConnection(clipper->GetOutputPort());
374 }
375 else
376 {
377 shadingMesh->SetInputData(polyData);
378 }
379 clippingEnabled = clipSurface->isChecked();
380 clipPositionSlider->setEnabled(clippingEnabled);
381 clipStatus->clear();
382 moveClipPlane();
383 }
384 catch(const std::exception &error)
385 {
386 const QSignalBlocker blocker(clipSurface);
387 clipSurface->setChecked(clippingEnabled);
388 clipStatus->setText(QString::fromStdString(error.what()) + " Previous clipping settings retained.");
389 }
390 }
391
392 void SurfaceMeshActor::reportError(const std::exception &error)
393 {
394 validDisplacement = false;
395 dirty = true;
396 actor->SetVisibility(false);
397 updateLighting();
398 status->setText(QString::fromStdString(error.what()));
399 }
400
401 std::set<size_t> SurfaceMeshActor::selectedFaceIDs() const
402 {
403 std::set<size_t> result;
404 const auto append=[&](const auto* group,const auto& checks)
405 {
406 if(group->isChecked())
407 {
408 for(const auto& entry : checks)
409 {
410 if(entry.second->isChecked())
411 {
412 result.insert(entry.first);
413 }
414 }
415 }
416 };
417 append(externalFaces,externalFaceChecks);
418 append(internalFaces,internalFaceChecks);
419 return result;
420 }
421
423 {
424 try
425 {
426 bool ok = false;
427 const double area = meshSizeEdit->text().toDouble(&ok);
428 if(!ok)
429 {
430 throw std::invalid_argument("Enter a positive maximum triangle area in b²");
431 }
432 auto replacement = SurfaceMeshGeometry::build(defectiveCrystal.ddBase.mesh, area, selectedFaceIDs());
433 referenceGeometry = std::move(replacement);
434 geometry = referenceGeometry;
435 displacement.clear();
436 validDisplacement = false;
437 dirty = true;
438 if(showSurface->isChecked() && frameAvailable)
439 {
440 compute();
441 }
442 else
443 {
444 status->setText(QString("%1 reference nodes; %2 triangles. Enable surface to compute displacement.")
445 .arg(geometry.referencePoints.size())
446 .arg(geometry.triangles.size()));
447 }
448 }
449 catch(const std::exception &error)
450 {
451 reportError(error);
452 }
453 render();
454 }
455
456 void SurfaceMeshActor::compute()
457 {
458 try
459 {
460 if(!dirty)
461 {
462 return;
463 }
464 if(referenceGeometry.referencePoints.empty())
465 {
466 bool ok = false;
467 const double area = meshSizeEdit->text().toDouble(&ok);
468 if(!ok)
469 {
470 throw std::invalid_argument("Enter a positive maximum triangle area in b²");
471 }
472 referenceGeometry = SurfaceMeshGeometry::build(defectiveCrystal.ddBase.mesh, area, selectedFaceIDs());
473 }
474 std::vector<SurfaceMeshGeometry::Trace> traces;
475 if(defectiveCrystal.ddBase.dislocationDynamicsBase)
476 {
477 for(const auto &entry : defectiveCrystal.ddBase.dislocationDynamics().glidePlaneFactory.glidePlanes())
478 {
479 const auto plane = entry.second.lock();
480 if(!plane)
481 {
482 continue;
483 }
484 SurfaceMeshGeometry::Trace trace{plane->P, plane->unitNormal, {}};
485 for(const auto &segment : plane->meshIntersections)
486 {
487 for(const auto *face : segment->faces)
488 {
489 trace.faces.insert(face->simplices().begin(), face->simplices().end());
490 }
491 }
492 if(!trace.faces.empty())
493 {
494 traces.push_back(std::move(trace));
495 }
496 }
497 }
498 geometry = referenceGeometry.split(traces);
499 // Snapshot UI selections and FEM parents before starting worker threads.
500 // The current configuration stays fixed until this synchronous call returns.
501 std::vector<const MicrostructureBase<3> *> selected;
502 for(size_t m = 0; m < microstructuresCheck.size(); ++m)
503 {
504 if(microstructuresCheck[m]->isChecked())
505 {
506 selected.push_back(defectiveCrystal.microstructures()[m].get());
507 }
508 }
509 std::vector<const MicrostructureBase<3>::ElementType *> elements(geometry.referencePoints.size(), nullptr);
510 if(defectiveCrystal.ddBase.fe)
511 {
512 for(size_t k = 0; k < elements.size(); ++k)
513 {
514 elements[k] = &defectiveCrystal.ddBase.fe->elements().at(geometry.parents[k]->xID);
515 }
516 }
517 std::vector<Eigen::Vector3d> next(geometry.referencePoints.size());
518 std::exception_ptr failure;
519 const auto count = static_cast<std::ptrdiff_t>(next.size());
520 // Each worker owns one result slot. Qt, VTK, geometry construction,
521 // and rendering remain on the calling GUI thread.
522#ifdef _OPENMP
523#pragma omp parallel for schedule(static) if(count >= 256 && !selected.empty())
524#endif
525 for(std::ptrdiff_t k = 0; k < count; ++k)
526 {
527 try
528 {
529 const auto selectedDisplacement = [&](const Eigen::Vector3d &point) -> Eigen::Vector3d
530 {
531 Eigen::Vector3d value = Eigen::Vector3d::Zero();
532 for(const auto *microstructure : selected)
533 {
534 value += microstructure->displacement(point, nullptr, elements[k], geometry.parents[k]);
535 }
536 return value;
537 };
538 Eigen::Vector3d u = selectedDisplacement(geometry.samplePoints[k]);
539 const Eigen::Vector3d offset = geometry.samplePoints[k] - geometry.referencePoints[k];
540 if(offset.squaredNorm() > 0.0)
541 {
542 // Both one-sided samples belong to the same worker.
543 u = 2.0 * u - selectedDisplacement(geometry.referencePoints[k] + 2.0 * offset);
544 }
545 if(!u.allFinite())
546 {
547 throw std::runtime_error("Nonfinite displacement on surface; adjust mesh size");
548 }
549 next[k] = u;
550 }
551 catch(...)
552 {
553 // Exceptions cannot escape an OpenMP worker. Preserve the
554 // first failure and report it after all workers have joined.
555#ifdef _OPENMP
556#pragma omp critical(surfaceMeshDisplacementFailure)
557#endif
558 {
559 if(!failure)
560 {
561 failure = std::current_exception();
562 }
563 }
564 }
565 }
566 if(failure)
567 {
568 std::rethrow_exception(failure);
569 }
570 displacement = std::move(next);
571 applyDisplacement();
572 dirty = false;
573 validDisplacement = true;
574 actor->SetVisibility(showSurface->isChecked());
575 updateLighting();
576 status->setText(QString("%1 nodes; %2 surface triangles; %3 wall triangles.")
577 .arg(geometry.referencePoints.size())
578 .arg(geometry.triangles.size())
579 .arg(geometry.walls.size()));
580 }
581 catch(const std::exception &error)
582 {
583 reportError(error);
584 }
585 }
586
587 void SurfaceMeshActor::applyDisplacement()
588 {
589 const auto moved = geometry.displaced(displacement, amplification->value());
590 auto points = vtkSmartPointer<vtkPoints>::New();
591 points->SetDataTypeToDouble();
592 for(const auto &point : moved)
593 {
594 points->InsertNextPoint(point.data());
595 }
596 auto cells = vtkSmartPointer<vtkCellArray>::New();
597 for(const auto &triangle : geometry.triangles)
598 {
599 const vtkIdType ids[3] = {static_cast<vtkIdType>(triangle[0]), static_cast<vtkIdType>(triangle[1]),
600 static_cast<vtkIdType>(triangle[2])};
601 cells->InsertNextCell(3, ids);
602 }
603 for(const auto &triangle : geometry.walls)
604 {
605 const auto &a = moved[triangle[0]];
606 const auto &b = moved[triangle[1]];
607 const auto &c = moved[triangle[2]];
608 if((b - a).cross(c - a).squaredNorm() > 0.0)
609 {
610 const vtkIdType ids[3] = {static_cast<vtkIdType>(triangle[0]), static_cast<vtkIdType>(triangle[1]),
611 static_cast<vtkIdType>(triangle[2])};
612 cells->InsertNextCell(3, ids);
613 }
614 }
615 polyData->SetPoints(points);
616 polyData->SetPolys(cells);
617 polyData->Modified();
618 normals->Update();
619 renderer->ResetCameraClippingRange();
620 }
621
623 {
624 frameAvailable = true;
625 dirty = true;
626 validDisplacement = false;
627 if(showSurface->isChecked())
628 {
629 compute();
630 }
631 }
632} // namespace model
const Eigen::Matrix< double, _dim, 1 > & xMax() const
const Eigen::Matrix< double, _dim, 1 > & xMin() const
const auto & regions() const
const vtkSmartPointer< vtkLight > reliefLight
const vtkSmartPointer< vtkPolyDataMapper > mapper
std::vector< QCheckBox * > microstructuresCheck
QLineEdit *const clipNormalEdit
QSlider *const clipPositionSlider
SurfaceMeshActor(vtkGenericOpenGLRenderWindow *window, vtkRenderer *renderer, const DefectiveCrystal< 3 > &crystal)
const vtkSmartPointer< vtkPolyData > polyData
QGroupBox *const externalFaces
QCheckBox *const clipSurface
const vtkSmartPointer< vtkPlane > clipPlane
const vtkSmartPointer< vtkPolyDataNormals > normals
QGroupBox *const internalFaces
QLineEdit *const clipCenterEdit
QCheckBox *const showEdges
const vtkSmartPointer< vtkCleanPolyData > shadingMesh
QCheckBox *const showSurface
QLineEdit *const meshSizeEdit
const vtkSmartPointer< vtkActor > actor
const vtkSmartPointer< vtkClipPolyData > clipper
QDoubleSpinBox *const amplification
c
Definition int_R.m:15
a
Definition int_R.m:14
b
Definition int_R.m:16
QGridLayout * grid(QGroupBox *group)
QGroupBox * box(const QString &title, QWidget *parent, const QString &description, QCheckBox *toggle=nullptr)
void field(QGridLayout *layout, int row, const QString &title, QWidget *input, const QString &description)
size_t InstanceCount< Derived >::count
const SimplicialMesh< dim > & mesh
DefectiveCrystalBase< dim > & ddBase
std::vector< Vector > referencePoints
static SurfaceMeshGeometry build(const SimplicialMesh< 3 > &mesh, double maxArea)
void check(bool ok, const char *message)
int elements
REAL ** triangle
Definition triangle.cpp:508
REAL u[4]
int index
int k
REAL area
result
void t struct mesh * m