21 : renderWindow(window), renderer(rendererIn), defectiveCrystal(crystal),
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())
36 amplification->setObjectName(
"surfaceDisplacementAmplification");
42 "Maximum reference triangle area in b², as in the Fields tab. Smaller values resolve finer steps.");
46 amplification->setToolTip(
"Displayed position = reference position + amplification × displacement. Zero shows "
47 "the reference surface.");
48 auto *layout =
new QGridLayout(
this);
50 "Surface",
this,
"Show the displaced selected mesh faces, including sharp slip-step walls.",
showSurface);
52 layout->addWidget(surfaceBox, 0, 0);
54 "Maximum reference triangle area in b squared. Smaller values give finer detail and more "
55 "computation. Press Enter to rebuild.");
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.");
62 controls::box(
"Physics",
this,
"Select which simulated microstructures contribute to displacement.");
63 surfaceLayout->addWidget(physicsBox, 3, 0, 1, 2);
65 for(
const auto µstructure : defectiveCrystal.microstructures())
67 auto *
check =
new QCheckBox(QString::fromStdString(microstructure->tag),
this);
68 check->setChecked(
true);
71 check->setToolTip(
"Include this physics contribution in the surface displacement. Changes recompute the "
73 connect(
check, &QCheckBox::toggled,
this,
77 validDisplacement =
false;
87 QString(
"%1 %2 %3").arg(center.x(), 0,
'g', 16).arg(center.y(), 0,
'g', 16).arg(center.z(), 0,
'g', 16));
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.");
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);
100 "Global x y z coordinates of the reference clip-plane center, in b. Press Enter to apply and "
101 "reset the offset.");
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.");
106 appliedClipCenter = center;
113 clipPositionSlider->setToolTip(
"Move along the applied normal. Zero returns to the entered center; range is "
114 "one box diagonal in each direction.");
117 connect(
clipPositionSlider, &QSlider::valueChanged,
this, &SurfaceMeshActor::moveClipPlane);
119 clipLayout->addWidget(
clipStatus, row++, 0, 1, 2);
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.");
141 for(
const auto& face : region.second->faces())
143 const size_t id=face.second->sID;
144 const bool external=face.second->isExternal();
145 auto&
checks=external?externalFaceChecks:internalFaceChecks;
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));
157 const auto populate=[
this](
const auto&
checks,QGridLayout* grid)
160 for(
const auto& entry :
checks)
162 grid->addWidget(entry.second,
index/2,
index%2);
167 populate(externalFaceChecks,externalLayout);
168 populate(internalFaceChecks,internalLayout);
171 const auto connectFaceGroup=[
this](
auto* group,
const auto*
checks)
173 connect(group,&QGroupBox::toggled,
this,[
this,
checks,firstEnable=
true](
bool enabled)
mutable
175 if(enabled && firstEnable)
177 for(
const auto& entry : *
checks)
179 const QSignalBlocker blocker(entry.second);
180 entry.second->setChecked(
true);
189 status->setWordWrap(
true);
190 layout->addWidget(
status, 1, 0);
191 layout->setRowStretch(2, 1);
202 normals->ComputePointNormalsOn();
204 normals->SetFeatureAngle(30.0);
205 normals->ComputeCellNormalsOn();
207 normals->AutoOrientNormalsOff();
209 mapper->ScalarVisibilityOff();
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})
225 coefficient->setRange(0.0, 1.0);
226 coefficient->setDecimals(2);
227 coefficient->setSingleStep(0.05);
229 ambient->setValue(
actor->GetProperty()->GetAmbient());
230 diffuse->setValue(
actor->GetProperty()->GetDiffuse());
231 specular->setValue(
actor->GetProperty()->GetSpecular());
233 "Uniform illumination from 0 to 1. Increase to soften dark regions and reduce orientation contrast. Changes apply immediately.");
235 "Directional matte lighting from 0 to 1. Higher values emphasize surface slope relative to the light. Changes apply immediately.");
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]()
240 actor->GetProperty()->SetAmbient(ambient->value());
241 actor->GetProperty()->SetDiffuse(diffuse->value());
242 actor->GetProperty()->SetSpecular(specular->value());
245 for(
auto *coefficient : {ambient, diffuse, specular})
247 connect(coefficient, &QDoubleSpinBox::valueChanged,
this, updateShading);
249 actor->GetProperty()->SetInterpolationToPhong();
250 actor->GetProperty()->SetEdgeColor(0.15, 0.15, 0.15);
251 actor->SetVisibility(
false);
252 renderer->AddActor(
actor);
261 status->setText(
"Enable the surface after loading a frame.");
266 if(enabled && frameAvailable)
270 actor->SetVisibility(enabled && validDisplacement);
273 connect(
showEdges, &QCheckBox::toggled,
this,
276 actor->GetProperty()->SetEdgeVisibility(enabled);
282 if(validDisplacement)
288 catch(
const std::exception &error)