...
 
Commits (3)
......@@ -75,22 +75,108 @@
</para>
</simplesect>
<simplesect>
<title>Two-dimensional systems</title>
<para>
Depending on the selected <link linkend="scene_objects.simulation_cell">dimensionality</link> of the input simulation cell,
the atomic strain calculation is either performed in 2D or 3D mode. In 3D mode, all 9 components of the atomic
deformation gradient tensor are calculated from the relative motion vectors of neighboring particles using
a least-squares fit. In 2D mode, only 4 independent components are calculated:
<informaltable>
<tgroup cols="3">
<thead>
<row>
<entry></entry>
<entry>3D mode</entry>
<entry>2D mode</entry>
</row>
</thead>
<tbody>
<row>
<entry>Deformation gradient <mathphrase>F</mathphrase></entry>
<entry><screen>
<mathphrase>F<subscript>xx</subscript></mathphrase> <mathphrase>F<subscript>xy</subscript></mathphrase> <mathphrase>F<subscript>xz</subscript></mathphrase>
<mathphrase>F<subscript>yx</subscript></mathphrase> <mathphrase>F<subscript>yy</subscript></mathphrase> <mathphrase>F<subscript>yz</subscript></mathphrase>
<mathphrase>F<subscript>zx</subscript></mathphrase> <mathphrase>F<subscript>zy</subscript></mathphrase> <mathphrase>F<subscript>zz</subscript></mathphrase>
</screen>
</entry>
<entry><screen>
<mathphrase>F<subscript>xx</subscript></mathphrase> <mathphrase>F<subscript>xy</subscript></mathphrase> 0
<mathphrase>F<subscript>yx</subscript></mathphrase> <mathphrase>F<subscript>yy</subscript></mathphrase> 0
0 0 1
</screen>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</simplesect>
<simplesect>
<title>More on the micromechanical quantities</title>
<para>
The atomic strain calculation uses finite-strain theory. Accordingly, strain is measured using the
The atomic strain calculation in OVITO is based on finite-strain theory. Accordingly, strain is measured using the
<link xlink:href="https://en.wikipedia.org/wiki/Finite_strain_theory#Finite_strain_tensors">Green-Lagrangian strain tensor</link>
<inlineequation><mathphrase>E=1/2(F<superscript>T</superscript>F-I)</mathphrase></inlineequation>.
On the basis of this symmetric strain tensor, the modifier also calculates the <emphasis>von Mises</emphasis> local shear invariant and the
volumetric (hydrostatic) part of the strain tensor and outputs these scalar quantities as particle properties named
<literal>Shear Strain</literal> and <literal>Volumetric Strain</literal>, respectively. The former is a good measure for shear deformations
in arbitrary directions.
along arbitrary directions.
<informaltable>
<tgroup cols="3">
<thead>
<row>
<entry></entry>
<entry>3D mode</entry>
<entry>2D mode</entry>
</row>
</thead>
<tbody>
<row>
<entry>Shear strain</entry>
<entry>
<inlineequation>
[<mathphrase>E<subscript>xy</subscript></mathphrase><superscript>2</superscript> +
<mathphrase>E<subscript>xz</subscript></mathphrase><superscript>2</superscript> +
<mathphrase>E<subscript>yz</subscript></mathphrase><superscript>2</superscript> +
1/6
((<mathphrase>E<subscript>xx</subscript></mathphrase> - <mathphrase>E<subscript>yy</subscript></mathphrase>)<superscript>2</superscript> +
(<mathphrase>E<subscript>xx</subscript></mathphrase> - <mathphrase>E<subscript>zz</subscript></mathphrase>)<superscript>2</superscript> +
(<mathphrase>E<subscript>yy</subscript></mathphrase> - <mathphrase>E<subscript>zz</subscript></mathphrase>)<superscript>2</superscript>)]
<superscript>1/2</superscript>
</inlineequation>
</entry>
<entry>
<inlineequation>
[<mathphrase>E<subscript>xy</subscript></mathphrase><superscript>2</superscript> +
1/2 (<mathphrase>E<subscript>xx</subscript></mathphrase> - <mathphrase>E<subscript>yy</subscript></mathphrase>)<superscript>2</superscript>]
<superscript>1/2</superscript>
</inlineequation>
</entry>
</row>
<row>
<entry>Volumetric strain</entry>
<entry>
<inlineequation>
(<mathphrase>E<subscript>xx</subscript></mathphrase> + <mathphrase>E<subscript>yy</subscript></mathphrase> + <mathphrase>E<subscript>zz</subscript></mathphrase>) / 3
</inlineequation>
</entry>
<entry>
<inlineequation>
(<mathphrase>E<subscript>xx</subscript></mathphrase> + <mathphrase>E<subscript>yy</subscript></mathphrase>) / 2
</inlineequation>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
<para>
The deformation gradient and the strain tensor both can only describe material deformations that are locally affine.
In fact, the atomic deformation gradient tensor is computed such that it best describes only the affine component of the
true deformation, which may actually be non-affine within the spherical sampling volumes. Falk &amp; Langer (see reference below) have proposed a measure to
quantify the deviation of the true deformation of the particle system from the affine approximation that the local deformation
quantify the deviation of the true deformation from the affine approximation that the atomic deformation
gradient represents. This measure, the <emphasis>non-affine squared displacement</emphasis> (<inlineequation><mathphrase>D<superscript>2</superscript><subscript>min</subscript></mathphrase></inlineequation>),
is basically the <link xlink:href="http://li.mit.edu/A/Graphics/A/annotate_atomic_strain/Doc/main.pdf">residual of the least-squares fit</link> through which the deformation gradient is calculated.
It may be used as a diagnostic for identifying local irreversible shear transformations in amorphous solids, for example.
......@@ -100,7 +186,7 @@
Falk and Langer, which consists of summing up the squared differences between the actual displacements of neighbors and
the computed affine approximation. In the current implementation, this sum does <emphasis>not</emphasis> get divided by the
number of neighbors within the cutoff range. Thus, the calculated <inlineequation><mathphrase>D<superscript>2</superscript><subscript>min</subscript></mathphrase></inlineequation>
value for a particle will depend on the number of neighbors that are included in the selected cutoff volume and will naturally rise
value for a particle is not normalized and will depend on the number of neighbors that are included in the selected cutoff volume. That means <inlineequation><mathphrase>D<superscript>2</superscript><subscript>min</subscript></mathphrase></inlineequation> values will naturally rise
if you increase the cutoff radius.
</para>
......@@ -110,8 +196,8 @@
<inlineequation><mathphrase>U</mathphrase></inlineequation> is symmetric positive definite. The local rotation <inlineequation><mathphrase>R</mathphrase></inlineequation> is encoded
as a quaternion <inlineequation>R=(X, Y, Z, W)</inlineequation> and is output by the modifier
as a particle property named <literal>Rotation</literal>. From this, the angle of rotation (in radians) for each particle can subsequently be
obtained as <inlineequation><mathphrase>theta=2*acos(Rotation.W)</mathphrase></inlineequation>,
e.g., using the <link linkend="particles.modifiers.compute_property">Compute Property modifier</link>.
obtained as <inlineequation><mathphrase>theta=2*acos(Rotation.W)</mathphrase></inlineequation>
using e.g. the <link linkend="particles.modifiers.compute_property">Compute Property</link> modifier.
</para>
</simplesect>
......
......@@ -57,8 +57,8 @@ public:
public:
/// Constructor.
ArrowPrimitive(Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality) :
_shape(shape), _shadingMode(shadingMode), _renderingQuality(renderingQuality) {}
ArrowPrimitive(Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality, bool translucentElements) :
_shape(shape), _shadingMode(shadingMode), _renderingQuality(renderingQuality), _translucentElements(translucentElements) {}
/// \brief Allocates a geometry buffer with the given number of elements.
virtual void startSetElements(int elementCount) = 0;
......@@ -89,6 +89,9 @@ public:
/// \brief Returns the selected element shape.
Shape shape() const { return _shape; }
/// \brief Returns whether elements are displayed as semi-transparent if their alpha color value is smaller than one.
bool translucentElements() const { return _translucentElements; }
private:
/// Controls the shading.
......@@ -100,6 +103,9 @@ private:
/// The shape of the elements.
Shape _shape;
/// Indicates whether some of the elements may be semi-transparent.
/// If false, the alpha color value is ignored.
bool _translucentElements;
};
OVITO_END_INLINE_NAMESPACE
......
......@@ -133,7 +133,7 @@ private:
/// Controls the shape of particles.
ParticleShape _particleShape;
/// Determines whether particles may be semi-transparent.
/// Indicates whether some of the particles may be semi-transparent.
/// If false, the alpha color value is ignored.
bool _translucentParticles;
};
......@@ -147,5 +147,3 @@ Q_DECLARE_METATYPE(Ovito::ParticlePrimitive::ParticleShape);
Q_DECLARE_TYPEINFO(Ovito::ParticlePrimitive::ShadingMode, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(Ovito::ParticlePrimitive::RenderingQuality, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(Ovito::ParticlePrimitive::ParticleShape, Q_PRIMITIVE_TYPE);
......@@ -168,7 +168,8 @@ public:
/// Requests a new arrow geometry buffer from the renderer.
virtual std::shared_ptr<ArrowPrimitive> createArrowPrimitive(ArrowPrimitive::Shape shape,
ArrowPrimitive::ShadingMode shadingMode = ArrowPrimitive::NormalShading,
ArrowPrimitive::RenderingQuality renderingQuality = ArrowPrimitive::MediumQuality) = 0;
ArrowPrimitive::RenderingQuality renderingQuality = ArrowPrimitive::MediumQuality,
bool translucentElements = false) = 0;
/// Requests a new triangle mesh geometry buffer from the renderer.
virtual std::shared_ptr<MeshPrimitive> createMeshPrimitive() = 0;
......
......@@ -45,8 +45,7 @@ public:
public:
/// Constructor.
DefaultArrowPrimitive(ArrowPrimitive::Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality) :
ArrowPrimitive(shape, shadingMode, renderingQuality) {}
using ArrowPrimitive::ArrowPrimitive;
/// \brief Allocates a geometry buffer with the given number of elements.
virtual void startSetElements(int elementCount) override {
......
......@@ -35,7 +35,7 @@ class OVITO_CORE_EXPORT DefaultImagePrimitive : public ImagePrimitive
public:
/// Constructor.
DefaultImagePrimitive() {}
using ImagePrimitive::ImagePrimitive;
/// \brief Returns true if the geometry buffer is filled and can be rendered with the given renderer.
virtual bool isValid(SceneRenderer* renderer) override;
......
......@@ -35,7 +35,7 @@ class OVITO_CORE_EXPORT DefaultLinePrimitive : public LinePrimitive
public:
/// Constructor.
DefaultLinePrimitive() {}
using LinePrimitive::LinePrimitive;
/// \brief Allocates a geometry buffer with the given number of vertices.
virtual void setVertexCount(int vertexCount, FloatType lineWidth) override {
......
......@@ -35,7 +35,7 @@ class OVITO_CORE_EXPORT DefaultMarkerPrimitive : public MarkerPrimitive
public:
/// Constructor.
DefaultMarkerPrimitive(MarkerShape shape) : MarkerPrimitive(shape) {}
using MarkerPrimitive::MarkerPrimitive;
/// \brief Allocates a geometry buffer with the given number of markers.
virtual void setCount(int markerCount) override {
......
......@@ -36,7 +36,7 @@ class OVITO_CORE_EXPORT DefaultMeshPrimitive : public MeshPrimitive
public:
/// Constructor.
DefaultMeshPrimitive() {}
using MeshPrimitive::MeshPrimitive;
/// Sets the mesh to be stored in this buffer object.
virtual void setMesh(const TriMesh& mesh, const ColorA& meshColor) override {
......
......@@ -35,8 +35,7 @@ class OVITO_CORE_EXPORT DefaultParticlePrimitive : public ParticlePrimitive
public:
/// Constructor.
DefaultParticlePrimitive(ShadingMode shadingMode, RenderingQuality renderingQuality, ParticleShape shape, bool translucentParticles) :
ParticlePrimitive(shadingMode, renderingQuality, shape, translucentParticles) {}
using ParticlePrimitive::ParticlePrimitive;
/// \brief Allocates a geometry buffer with the given number of particles.
virtual void setSize(int particleCount) override {
......
......@@ -35,7 +35,7 @@ class OVITO_CORE_EXPORT DefaultTextPrimitive : public TextPrimitive
public:
/// Constructor.
DefaultTextPrimitive() {}
using TextPrimitive::TextPrimitive;
/// \brief Returns true if the geometry buffer is filled and can be rendered with the given renderer.
virtual bool isValid(SceneRenderer* renderer) override;
......
......@@ -92,8 +92,9 @@ public:
virtual std::shared_ptr<ArrowPrimitive> createArrowPrimitive(
ArrowPrimitive::Shape shape,
ArrowPrimitive::ShadingMode shadingMode,
ArrowPrimitive::RenderingQuality renderingQuality) override {
return std::make_shared<DefaultArrowPrimitive>(shape, shadingMode, renderingQuality);
ArrowPrimitive::RenderingQuality renderingQuality,
bool translucentElements) override {
return std::make_shared<DefaultArrowPrimitive>(shape, shadingMode, renderingQuality, translucentElements);
}
/// Requests a new triangle mesh buffer from the renderer.
......
......@@ -442,7 +442,7 @@ void PickOrbitCenterMode::renderOverlay3D(Viewport* vp, ViewportSceneRenderer* r
if(!renderer->isBoundingBoxPass()) {
// Create line buffer.
if(!_orbitCenterMarker || !_orbitCenterMarker->isValid(renderer)) {
_orbitCenterMarker = renderer->createArrowPrimitive(ArrowPrimitive::CylinderShape, ArrowPrimitive::NormalShading, ArrowPrimitive::HighQuality);
_orbitCenterMarker = renderer->createArrowPrimitive(ArrowPrimitive::CylinderShape, ArrowPrimitive::NormalShading, ArrowPrimitive::HighQuality, false);
_orbitCenterMarker->startSetElements(3);
_orbitCenterMarker->setElement(0, Point3(-1,0,0), Vector3(2,0,0), ColorA(1,0,0), FloatType(0.05));
_orbitCenterMarker->setElement(1, Point3(0,-1,0), Vector3(0,2,0), ColorA(0,1,0), FloatType(0.05));
......
......@@ -28,8 +28,8 @@ namespace Ovito { OVITO_BEGIN_INLINE_NAMESPACE(Rendering) OVITO_BEGIN_INLINE_NAM
/******************************************************************************
* Constructor.
******************************************************************************/
OpenGLArrowPrimitive::OpenGLArrowPrimitive(OpenGLSceneRenderer* renderer, ArrowPrimitive::Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality) :
ArrowPrimitive(shape, shadingMode, renderingQuality),
OpenGLArrowPrimitive::OpenGLArrowPrimitive(OpenGLSceneRenderer* renderer, ArrowPrimitive::Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality, bool translucentElements) :
ArrowPrimitive(shape, shadingMode, renderingQuality, translucentElements),
_contextGroup(QOpenGLContextGroup::currentContextGroup()),
_elementCount(-1), _cylinderSegments(16), _verticesPerElement(0),
_mappedVerticesWithNormals(nullptr), _mappedVerticesWithElementInfo(nullptr),
......@@ -556,8 +556,22 @@ void OpenGLArrowPrimitive::render(SceneRenderer* renderer)
if(_elementCount <= 0 || !vpRenderer)
return;
// If object is translucent, don't render it during the first rendering pass.
// Queue primitive so that it gets rendered during the second pass.
if(!renderer->isPicking() && translucentElements() && vpRenderer->translucentPass() == false) {
vpRenderer->registerTranslucentPrimitive(shared_from_this());
return;
}
vpRenderer->rebindVAO();
// Activate blend mode when rendering translucent elements.
if(!vpRenderer->isPicking() && translucentElements()) {
vpRenderer->glEnable(GL_BLEND);
vpRenderer->glBlendEquation(GL_FUNC_ADD);
vpRenderer->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_COLOR, GL_ONE);
}
if(shadingMode() == NormalShading) {
if(renderingQuality() == HighQuality && shape() == CylinderShape)
renderWithElementInfo(vpRenderer);
......@@ -568,6 +582,10 @@ void OpenGLArrowPrimitive::render(SceneRenderer* renderer)
renderWithElementInfo(vpRenderer);
}
OVITO_REPORT_OPENGL_ERRORS();
// Deactivate blend mode after rendering translucent elements.
if(!vpRenderer->isPicking() && translucentElements())
vpRenderer->glDisable(GL_BLEND);
}
/******************************************************************************
......
......@@ -31,12 +31,12 @@ namespace Ovito { OVITO_BEGIN_INLINE_NAMESPACE(Rendering) OVITO_BEGIN_INLINE_NAM
/**
* \brief Buffer object that stores a set of arrows to be rendered in the viewports.
*/
class OpenGLArrowPrimitive : public ArrowPrimitive
class OpenGLArrowPrimitive : public ArrowPrimitive, public std::enable_shared_from_this<OpenGLArrowPrimitive>
{
public:
/// Constructor.
OpenGLArrowPrimitive(OpenGLSceneRenderer* renderer, ArrowPrimitive::Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality);
OpenGLArrowPrimitive(OpenGLSceneRenderer* renderer, ArrowPrimitive::Shape shape, ShadingMode shadingMode, RenderingQuality renderingQuality, bool translucentElements);
/// \brief Allocates a geometry buffer with the given number of elements.
virtual void startSetElements(int elementCount) override;
......
......@@ -469,11 +469,12 @@ std::shared_ptr<ImagePrimitive> OpenGLSceneRenderer::createImagePrimitive()
******************************************************************************/
std::shared_ptr<ArrowPrimitive> OpenGLSceneRenderer::createArrowPrimitive(ArrowPrimitive::Shape shape,
ArrowPrimitive::ShadingMode shadingMode,
ArrowPrimitive::RenderingQuality renderingQuality)
ArrowPrimitive::RenderingQuality renderingQuality,
bool translucentElements)
{
OVITO_ASSERT(!isBoundingBoxPass());
makeContextCurrent();
return std::make_shared<OpenGLArrowPrimitive>(this, shape, shadingMode, renderingQuality);
return std::make_shared<OpenGLArrowPrimitive>(this, shape, shadingMode, renderingQuality, translucentElements);
}
/******************************************************************************
......
......@@ -94,7 +94,8 @@ public:
/// Requests a new arrow geometry buffer from the renderer.
virtual std::shared_ptr<ArrowPrimitive> createArrowPrimitive(ArrowPrimitive::Shape shape,
ArrowPrimitive::ShadingMode shadingMode,
ArrowPrimitive::RenderingQuality renderingQuality) override;
ArrowPrimitive::RenderingQuality renderingQuality,
bool translucentElements) override;
/// Requests a new triangle mesh buffer from the renderer.
virtual std::shared_ptr<MeshPrimitive> createMeshPrimitive() override;
......
......@@ -85,14 +85,18 @@ void DislocationAnalysisModifierEditor::createUI(const RolloutInsertionParameter
sublayout->setSpacing(4);
sublayout->setColumnStretch(0, 1);
// Color by type
BooleanParameterUI* colorByTypeUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::colorByType));
sublayout->addWidget(colorByTypeUI->checkBox(), 0, 0);
BooleanParameterUI* onlySelectedParticlesUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::onlySelectedParticles));
sublayout->addWidget(onlySelectedParticlesUI->checkBox(), 0, 0);
sublayout->addWidget(onlySelectedParticlesUI->checkBox(), 1, 0);
BooleanParameterUI* outputInterfaceMeshUI = new BooleanParameterUI(this, PROPERTY_FIELD(DislocationAnalysisModifier::outputInterfaceMesh));
sublayout->addWidget(outputInterfaceMeshUI->checkBox(), 1, 0);
sublayout->addWidget(outputInterfaceMeshUI->checkBox(), 2, 0);
BooleanParameterUI* onlyPerfectDislocationsUI = new BooleanParameterUI(this, PROPERTY_FIELD(DislocationAnalysisModifier::onlyPerfectDislocations));
sublayout->addWidget(onlyPerfectDislocationsUI->checkBox(), 2, 0);
sublayout->addWidget(onlyPerfectDislocationsUI->checkBox(), 3, 0);
// Status label.
layout->addWidget(statusLabel());
......@@ -159,8 +163,6 @@ void DislocationAnalysisModifierEditor::createUI(const RolloutInsertionParameter
DislocationTypeListParameterUI::DislocationTypeListParameterUI(QObject* parent)
: RefTargetListParameterUI(parent, PROPERTY_FIELD(StructurePattern::burgersVectorFamilies))
{
connect(tableWidget(220), &QTableWidget::doubleClicked, this, &DislocationTypeListParameterUI::onDoubleClickDislocationType);
tableWidget()->setAutoScroll(false);
}
......@@ -242,4 +244,3 @@ void DislocationTypeListParameterUI::onDoubleClickDislocationType(const QModelIn
} // End of namespace
} // End of namespace
} // End of namespace
......@@ -193,6 +193,14 @@ PYBIND11_MODULE(CrystalAnalysis, m)
"large Burgers circuits are needed to identify dissociated dislocations with a wide core. "
"\n\n"
":Default: False\n")
.def_property("color_by_type", &DislocationAnalysisModifier::colorByType, &DislocationAnalysisModifier::setColorByType,
"Controls whether the modifier assigns a color to each particle based on the identified structure type. "
"\n\n"
":Default: ``True``\n")
.def_property("only_selected", &DislocationAnalysisModifier::onlySelectedParticles, &DislocationAnalysisModifier::setOnlySelectedParticles,
"Lets the modifier perform the analysis only for selected particles. Particles that are not selected will be treated as if they did not exist."
"\n\n"
":Default: ``False``\n")
.def_property("output_interface_mesh", &DislocationAnalysisModifier::outputInterfaceMesh, &DislocationAnalysisModifier::setOutputInterfaceMesh)
;
......
......@@ -47,6 +47,10 @@ void BondAngleAnalysisModifierEditor::createUI(const RolloutInsertionParameters&
BooleanParameterUI* onlySelectedParticlesUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::onlySelectedParticles));
layout1->addWidget(onlySelectedParticlesUI->checkBox());
// Color by type
BooleanParameterUI* colorByTypeUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::colorByType));
layout1->addWidget(colorByTypeUI->checkBox());
// Status label.
layout1->addSpacing(10);
layout1->addWidget(statusLabel());
......
......@@ -77,6 +77,10 @@ void CommonNeighborAnalysisModifierEditor::createUI(const RolloutInsertionParame
BooleanParameterUI* onlySelectedParticlesUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::onlySelectedParticles));
layout1->addWidget(onlySelectedParticlesUI->checkBox());
// Color by type
BooleanParameterUI* colorByTypeUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::colorByType));
layout1->addWidget(colorByTypeUI->checkBox());
// Status label.
layout1->addSpacing(10);
layout1->addWidget(statusLabel());
......
......@@ -47,6 +47,10 @@ void IdentifyDiamondModifierEditor::createUI(const RolloutInsertionParameters& r
BooleanParameterUI* onlySelectedParticlesUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::onlySelectedParticles));
layout1->addWidget(onlySelectedParticlesUI->checkBox());
// Color by type
BooleanParameterUI* colorByTypeUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::colorByType));
layout1->addWidget(colorByTypeUI->checkBox());
// Status label.
layout1->addWidget(statusLabel());
......
......@@ -86,6 +86,10 @@ void PolyhedralTemplateMatchingModifierEditor::createUI(const RolloutInsertionPa
sublayout->addWidget(outputOrderingTypesUI->checkBox());
outputOrderingTypesUI->checkBox()->setText(tr("Ordering types"));
// Color by type
BooleanParameterUI* colorByTypeUI = new BooleanParameterUI(this, PROPERTY_FIELD(StructureIdentificationModifier::colorByType));
sublayout->addWidget(colorByTypeUI->checkBox());
StructureListParameterUI* structureTypesPUI = new StructureListParameterUI(this, true);
layout1->addSpacing(10);
layout1->addWidget(structureTypesPUI->tableWidget());
......
......@@ -116,12 +116,13 @@ std::vector<Color> ParticleInputHelper::inputBondColors(TimePoint time, TimeInte
}
// Query half-bond colors from vis element.
std::vector<Color> halfBondColors = bondsVis->halfBondColors(
std::vector<ColorA> halfBondColors = bondsVis->halfBondColors(
inputParticleCount(),
inputStandardProperty<BondProperty>(BondProperty::TopologyProperty),
inputStandardProperty<BondProperty>(BondProperty::ColorProperty),
inputStandardProperty<BondProperty>(BondProperty::TypeProperty),
inputStandardProperty<BondProperty>(BondProperty::SelectionProperty),
nullptr, // No transparency needed here
particleVis,
inputStandardProperty<ParticleProperty>(ParticleProperty::ColorProperty),
inputStandardProperty<ParticleProperty>(ParticleProperty::TypeProperty));
......@@ -131,7 +132,7 @@ std::vector<Color> ParticleInputHelper::inputBondColors(TimePoint time, TimeInte
std::vector<Color> colors(inputBondCount());
auto ci = halfBondColors.cbegin();
for(Color& co : colors) {
co = *ci;
co = Color(ci->r(), ci->g(), ci->b());
ci += 2;
}
return colors;
......
......@@ -33,15 +33,18 @@ IMPLEMENT_OVITO_CLASS(StructureIdentificationModifier);
IMPLEMENT_OVITO_CLASS(StructureIdentificationModifierApplication);
DEFINE_REFERENCE_FIELD(StructureIdentificationModifier, structureTypes);
DEFINE_PROPERTY_FIELD(StructureIdentificationModifier, onlySelectedParticles);
DEFINE_PROPERTY_FIELD(StructureIdentificationModifier, colorByType);
SET_PROPERTY_FIELD_LABEL(StructureIdentificationModifier, structureTypes, "Structure types");
SET_PROPERTY_FIELD_LABEL(StructureIdentificationModifier, onlySelectedParticles, "Use only selected particles");
SET_PROPERTY_FIELD_LABEL(StructureIdentificationModifier, colorByType, "Color particles by type");
SET_MODIFIER_APPLICATION_TYPE(StructureIdentificationModifier, StructureIdentificationModifierApplication);
/******************************************************************************
* Constructs the modifier object.
******************************************************************************/
StructureIdentificationModifier::StructureIdentificationModifier(DataSet* dataset) : AsynchronousModifier(dataset),
_onlySelectedParticles(false)
_onlySelectedParticles(false),
_colorByType(true)
{
}
......@@ -123,28 +126,40 @@ PipelineFlowState StructureIdentificationModifier::StructureIdentificationEngine
// Attach structure types to output particle property.
structureProperty->setElementTypes(modifier->structureTypes());
// Build structure type-to-color map.
std::vector<Color> structureTypeColors(modifier->structureTypes().size());
if(modifier->colorByType()) {
// Build structure type-to-color map.
std::vector<Color> structureTypeColors(modifier->structureTypes().size());
for(ElementType* stype : modifier->structureTypes()) {
OVITO_ASSERT(stype->id() >= 0);
if(stype->id() >= (int)structureTypeColors.size()) {
structureTypeColors.resize(stype->id() + 1);
}
structureTypeColors[stype->id()] = stype->color();
}
// Assign colors to particles based on their structure type.
ParticleProperty* colorProperty = poh.outputStandardProperty<ParticleProperty>(ParticleProperty::ColorProperty);
const int* s = structureProperty->constDataInt();
for(Color& c : colorProperty->colorRange()) {
if(*s >= 0 && *s < structureTypeColors.size()) {
c = structureTypeColors[*s];
}
else c.setWhite();
++s;
}
}
// Count the number of identified particles of each type.
std::vector<size_t> typeCounters(modifier->structureTypes().size(), 0);
for(ElementType* stype : modifier->structureTypes()) {
OVITO_ASSERT(stype->id() >= 0);
if(stype->id() >= (int)structureTypeColors.size()) {
structureTypeColors.resize(stype->id() + 1);
if(stype->id() >= (int)typeCounters.size())
typeCounters.resize(stype->id() + 1, 0);
}
structureTypeColors[stype->id()] = stype->color();
}
// Assign colors to particles based on their structure type.
ParticleProperty* colorProperty = poh.outputStandardProperty<ParticleProperty>(ParticleProperty::ColorProperty);
const int* s = structureProperty->constDataInt();
for(Color& c : colorProperty->colorRange()) {
if(*s >= 0 && *s < structureTypeColors.size()) {
c = structureTypeColors[*s];
typeCounters[*s]++;
}
else c.setWhite();
++s;
for(int t : structureProperty->constIntRange()) {
if(t >= 0 && t < typeCounters.size())
typeCounters[t]++;
}
// Store the per-type counts in the ModifierApplication.
......
......@@ -118,6 +118,14 @@ public:
/// Constructor.
StructureIdentificationModifier(DataSet* dataset);
/// This method indicates whether cached computation results of the modifier should be discarded whenever
/// a parameter of the modifier changes.
virtual bool discardResultsOnModifierChange(const PropertyFieldEvent& event) const override {
// Avoid a recomputation from scratch if the color-by-type option is being changed.
if(event.field() == &PROPERTY_FIELD(colorByType)) return false;
return AsynchronousModifier::discardResultsOnModifierChange(event);
}
protected:
/// Saves the class' contents to the given stream.
......@@ -142,6 +150,9 @@ private:
/// Controls whether analysis should take into account only selected particles.
DECLARE_MODIFIABLE_PROPERTY_FIELD(bool, onlySelectedParticles, setOnlySelectedParticles);
/// Controls whether the modifier colors particles based on their type.
DECLARE_MODIFIABLE_PROPERTY_FIELD(bool, colorByType, setColorByType);
};
......
......@@ -286,15 +286,27 @@ void AtomicStrainModifier::AtomicStrainEngine::computeStrain(size_t particleInde
// Calculate von Mises shear strain.
double xydiff = strain.xx() - strain.yy();
double xzdiff = strain.xx() - strain.zz();
double yzdiff = strain.yy() - strain.zz();
double shearStrain = sqrt(strain.xy()*strain.xy() + strain.xz()*strain.xz() + strain.yz()*strain.yz() +
(xydiff*xydiff + xzdiff*xzdiff + yzdiff*yzdiff) / 6.0);
double shearStrain;
if(!cell().is2D()) {
double xzdiff = strain.xx() - strain.zz();
double yzdiff = strain.yy() - strain.zz();
shearStrain = sqrt(strain.xy()*strain.xy() + strain.xz()*strain.xz() + strain.yz()*strain.yz() +
(xydiff*xydiff + xzdiff*xzdiff + yzdiff*yzdiff) / 6.0);
}
else {
shearStrain = sqrt(strain.xy()*strain.xy() + (xydiff*xydiff) / 2.0);
}
OVITO_ASSERT(std::isfinite(shearStrain));
shearStrains()->setFloat(particleIndex, (FloatType)shearStrain);
// Calculate volumetric component.
double volumetricStrain = (strain(0,0) + strain(1,1) + strain(2,2)) / 3.0;
double volumetricStrain;
if(!cell().is2D()) {
volumetricStrain = (strain(0,0) + strain(1,1) + strain(2,2)) / 3.0;
}
else {
volumetricStrain = (strain(0,0) + strain(1,1)) / 2.0;
}
OVITO_ASSERT(std::isfinite(volumetricStrain));
volumetricStrains()->setFloat(particleIndex, (FloatType)volumetricStrain);
......
......@@ -68,6 +68,7 @@ PropertyPtr BondProperty::OOMetaClass::createStandardStorage(size_t bondsCount,
stride = sizeof(int);
break;
case LengthProperty:
case TransparencyProperty:
dataType = PropertyStorage::Float;
componentCount = 1;
stride = sizeof(FloatType);
......@@ -149,6 +150,7 @@ void BondProperty::OOMetaClass::initialize()
registerStandardProperty(LengthProperty, tr("Length"), PropertyStorage::Float, emptyList);
registerStandardProperty(TopologyProperty, tr("Topology"), PropertyStorage::Int64, abList);
registerStandardProperty(PeriodicImageProperty, tr("Periodic Image"), PropertyStorage::Int, xyzList);
registerStandardProperty(TransparencyProperty, tr("Transparency"), PropertyStorage::Float, emptyList);
}
} // End of namespace
......
......@@ -76,6 +76,7 @@ public:
LengthProperty = PropertyStorage::FirstSpecificProperty,
TopologyProperty,
PeriodicImageProperty,
TransparencyProperty
};
public:
......
......@@ -141,6 +141,7 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
BondProperty* bondTypeProperty = BondProperty::findInState(flowState, BondProperty::TypeProperty);
BondProperty* bondColorProperty = BondProperty::findInState(flowState, BondProperty::ColorProperty);
BondProperty* bondSelectionProperty = BondProperty::findInState(flowState, BondProperty::SelectionProperty);
BondProperty* transparencyProperty = BondProperty::findInState(flowState, BondProperty::TransparencyProperty);
if(!useParticleColors()) {
particleColorProperty = nullptr;
particleTypeProperty = nullptr;
......@@ -163,6 +164,7 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
VersionedDataObjectRef, // Bond color property + revision number
VersionedDataObjectRef, // Bond type property + revision number
VersionedDataObjectRef, // Bond selection property + revision number
VersionedDataObjectRef, // Bond transparency + revision number
VersionedDataObjectRef, // Simulation cell + revision number
FloatType, // Bond width
Color, // Bond color
......@@ -180,6 +182,7 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
bondColorProperty,
bondTypeProperty,
bondSelectionProperty,
transparencyProperty,
simulationCell,
bondWidth(),
bondColor(),
......@@ -195,7 +198,7 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
if(bondTopologyProperty && positionProperty && bondRadius > 0) {
// Create bond geometry buffer.
arrowPrimitive = renderer->createArrowPrimitive(ArrowPrimitive::CylinderShape, shadingMode(), renderingQuality());
arrowPrimitive = renderer->createArrowPrimitive(ArrowPrimitive::CylinderShape, shadingMode(), renderingQuality(), transparencyProperty != nullptr);
arrowPrimitive->startSetElements((int)bondTopologyProperty->size() * 2);
// Obtain particle vis element.
......@@ -208,8 +211,8 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
}
// Determine half-bond colors.
std::vector<Color> colors = halfBondColors(positionProperty->size(), bondTopologyProperty,
bondColorProperty, bondTypeProperty, bondSelectionProperty,
std::vector<ColorA> colors = halfBondColors(positionProperty->size(), bondTopologyProperty,
bondColorProperty, bondTypeProperty, bondSelectionProperty, transparencyProperty,
particleVis, particleColorProperty, particleTypeProperty);
OVITO_ASSERT(colors.size() == arrowPrimitive->elementCount());
......@@ -229,12 +232,12 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
for(size_t k = 0; k < 3; k++)
if(int d = bondPeriodicImageProperty->getIntComponent(bondIndex, k)) vec += cell.column(k) * (FloatType)d;
}
arrowPrimitive->setElement(elementIndex++, positions[index1], vec * FloatType( 0.5), (ColorA)*color++, bondRadius);
arrowPrimitive->setElement(elementIndex++, positions[index2], vec * FloatType(-0.5), (ColorA)*color++, bondRadius);
arrowPrimitive->setElement(elementIndex++, positions[index1], vec * FloatType( 0.5), *color++, bondRadius);
arrowPrimitive->setElement(elementIndex++, positions[index2], vec * FloatType(-0.5), *color++, bondRadius);
}
else {
arrowPrimitive->setElement(elementIndex++, Point3::Origin(), Vector3::Zero(), (ColorA)*color++, 0);
arrowPrimitive->setElement(elementIndex++, Point3::Origin(), Vector3::Zero(), (ColorA)*color++, 0);
arrowPrimitive->setElement(elementIndex++, Point3::Origin(), Vector3::Zero(), *color++, 0);
arrowPrimitive->setElement(elementIndex++, Point3::Origin(), Vector3::Zero(), *color++, 0);
}
}
......@@ -263,22 +266,24 @@ void BondsVis::render(TimePoint time, DataObject* dataObject, const PipelineFlow
* Returns an array with two colors per full bond, because the two half-bonds
* may have different colors.
******************************************************************************/
std::vector<Color> BondsVis::halfBondColors(size_t particleCount, BondProperty* topologyProperty,
BondProperty* bondColorProperty, BondProperty* bondTypeProperty, BondProperty* bondSelectionProperty,
std::vector<ColorA> BondsVis::halfBondColors(size_t particleCount, BondProperty* topologyProperty,
BondProperty* bondColorProperty, BondProperty* bondTypeProperty, BondProperty* bondSelectionProperty, BondProperty* transparencyProperty,
ParticlesVis* particleDisplay, ParticleProperty* particleColorProperty, ParticleProperty* particleTypeProperty)
{
OVITO_ASSERT(topologyProperty != nullptr && topologyProperty->type() == BondProperty::TopologyProperty);
OVITO_ASSERT(bondColorProperty == nullptr || bondColorProperty->type() == BondProperty::ColorProperty);
OVITO_ASSERT(bondTypeProperty == nullptr || bondTypeProperty->type() == BondProperty::TypeProperty);
OVITO_ASSERT(bondSelectionProperty == nullptr || bondSelectionProperty->type() == BondProperty::SelectionProperty);
OVITO_ASSERT(transparencyProperty == nullptr || transparencyProperty->type() == BondProperty::TransparencyProperty);
std::vector<Color> output(topologyProperty->size() * 2);
std::vector<ColorA> output(topologyProperty->size() * 2);
ColorA defaultColor = (ColorA)bondColor();
if(bondColorProperty && bondColorProperty->size() * 2 == output.size()) {
// Take bond colors directly from the color property.
auto bc = output.begin();
for(const Color& c : bondColorProperty->constColorRange()) {
*bc++ = c;
*bc++ = c;
*bc++ = (ColorA)c;
*bc++ = (ColorA)c;
}
}
else if(useParticleColors() && particleDisplay != nullptr) {
......@@ -288,29 +293,28 @@ std::vector<Color> BondsVis::halfBondColors(size_t particleCount, BondProperty*
auto bond = topologyProperty->constDataInt64();
for(auto bc = output.begin(); bc != output.end(); bond += 2) {
if(bond[0] < particleCount && bond[1] < particleCount) {
*bc++ = particleColors[bond[0]];
*bc++ = particleColors[bond[1]];
*bc++ = (ColorA)particleColors[bond[0]];
*bc++ = (ColorA)particleColors[bond[1]];
}
else {
*bc++ = bondColor();
*bc++ = bondColor();
*bc++ = defaultColor;
*bc++ = defaultColor;
}
}
}
else {
Color defaultColor = bondColor();
if(bondTypeProperty && bondTypeProperty->size() * 2 == output.size()) {
// Assign colors based on bond types.
// Generate a lookup map for bond type colors.
const std::map<int,Color> colorMap = bondTypeProperty->typeColorMap();
std::array<Color,16> colorArray;
const std::map<int, Color>& colorMap = bondTypeProperty->typeColorMap();
std::array<ColorA,16> colorArray;
// Check if all type IDs are within a small, non-negative range.
// If yes, we can use an array lookup strategy. Otherwise we have to use a dictionary lookup strategy, which is slower.
if(std::all_of(colorMap.begin(), colorMap.end(),
[&colorArray](const std::map<int,Color>::value_type& i) { return i.first >= 0 && i.first < (int)colorArray.size(); })) {
[&colorArray](const std::map<int, ColorA>::value_type& i) { return i.first >= 0 && i.first < (int)colorArray.size(); })) {
colorArray.fill(defaultColor);
for(const auto& entry : colorMap)
colorArray[entry.first] = entry.second;
colorArray[entry.first] = (ColorA)entry.second;
// Fill color array.
const int* t = bondTypeProperty->constDataInt();
for(auto c = output.begin(); c != output.end(); ++t) {
......@@ -330,8 +334,8 @@ std::vector<Color> BondsVis::halfBondColors(size_t particleCount, BondProperty*
for(auto c = output.begin(); c != output.end(); ++t) {
auto it = colorMap.find(*t);
if(it != colorMap.end()) {
*c++ = it->second;
*c++ = it->second;
*c++ = (ColorA)it->second;
*c++ = (ColorA)it->second;
}
else {
*c++ = defaultColor;
......@@ -346,9 +350,18 @@ std::vector<Color> BondsVis::halfBondColors(size_t particleCount, BondProperty*
}
}
// Apply transparency values.
if(transparencyProperty && transparencyProperty->size() * 2 == output.size()) {
auto c = output.begin();
for(FloatType t : transparencyProperty->constFloatRange()) {
c->a() = t; ++c;
c->a() = t; ++c;
}
}
// Highlight selected bonds.
if(bondSelectionProperty && bondSelectionProperty->size() * 2 == output.size()) {
const Color selColor = selectionBondColor();
const ColorA selColor = (ColorA)selectionBondColor();
const int* t = bondSelectionProperty->constDataInt();
for(auto c = output.begin(); c != output.end(); ++t) {
if(*t) {
......
......@@ -56,8 +56,8 @@ public:
/// Determines the display colors of half-bonds.
/// Returns an array with two colors per full bond, because the two half-bonds may have different colors.
std::vector<Color> halfBondColors(size_t particleCount, BondProperty* topologyProperty,
BondProperty* bondColorProperty, BondProperty* bondTypeProperty, BondProperty* bondSelectionProperty,
std::vector<ColorA> halfBondColors(size_t particleCount, BondProperty* topologyProperty,
BondProperty* bondColorProperty, BondProperty* bondTypeProperty, BondProperty* bondSelectionProperty, BondProperty* transparencyProperty,
ParticlesVis* particleDisplay, ParticleProperty* particleColorProperty, ParticleProperty* particleTypeProperty);
public:
......
......@@ -367,6 +367,10 @@ void defineModifiersSubmodule(py::module m)
" * ``Color`` (:py:class:`~ovito.data.ParticleProperty`):\n"
" The modifier assigns a color to each particle according to its identified structure type. "
"\n")
.def_property("color_by_type", &BondAngleAnalysisModifier::colorByType, &BondAngleAnalysisModifier::setColorByType,
"Controls whether the modifier assigns a color to each particle based on the identified structure type. "
"\n\n"
":Default: ``True``\n")
;
expose_subobject_list(BondAngleAnalysisModifier_py, std::mem_fn(&StructureIdentificationModifier::structureTypes), "structures", "BondAngleAnalysisStructureTypeList",
"A list of :py:class:`~ovito.data.ParticleType` instances managed by this modifier, one for each supported structure type. "
......@@ -435,6 +439,10 @@ void defineModifiersSubmodule(py::module m)
"Lets the modifier perform the analysis only for selected particles. Particles that are not selected will be treated as if they did not exist."
"\n\n"
":Default: ``False``\n")
.def_property("color_by_type", &CommonNeighborAnalysisModifier::colorByType, &CommonNeighborAnalysisModifier::setColorByType,
"Controls whether the modifier assigns a color to each particle based on the identified structure type. "
"\n\n"
":Default: ``True``\n")
;
expose_subobject_list(CommonNeighborAnalysisModifier_py, std::mem_fn(&StructureIdentificationModifier::structureTypes), "structures", "CommonNeighborAnalysisStructureTypeList",
"A list of :py:class:`~ovito.data.ParticleType` instances managed by this modifier, one for each supported structure type. "
......@@ -500,6 +508,10 @@ void defineModifiersSubmodule(py::module m)
"Lets the modifier perform the analysis only for selected particles. Particles that are not selected will be treated as if they did not exist."
"\n\n"
":Default: ``False``\n")
.def_property("color_by_type", &IdentifyDiamondModifier::colorByType, &IdentifyDiamondModifier::setColorByType,
"Controls whether the modifier assigns a color to each particle based on the identified structure type. "
"\n\n"
":Default: ``True``\n")
;
expose_subobject_list(IdentifyDiamondModifier_py, std::mem_fn(&StructureIdentificationModifier::structureTypes), "structures", "IdentifyDiamondStructureTypeList",
"A list of :py:class:`~ovito.data.ParticleType` instances managed by this modifier, one for each supported structure type. "
......@@ -1159,6 +1171,10 @@ void defineModifiersSubmodule(py::module m)
"and will all be assigned to the \"Other\" structure category. "
"\n\n"
":Default: ``False``\n")
.def_property("color_by_type", &PolyhedralTemplateMatchingModifier::colorByType, &PolyhedralTemplateMatchingModifier::setColorByType,
"Controls whether the modifier assigns a color to each particle based on the identified structure type. "
"\n\n"
":Default: ``True``\n")
.def_property("output_rmsd", &PolyhedralTemplateMatchingModifier::outputRmsd, &PolyhedralTemplateMatchingModifier::setOutputRmsd,
"Boolean flag that controls whether the modifier outputs the computed per-particle RMSD values as a new particle property named ``RMSD``."
"\n\n"
......
......@@ -542,6 +542,7 @@ PYBIND11_MODULE(Particles, m)
"``BondProperty.Type.Length`` :guilabel:`Length` float \n"
"``BondProperty.Type.Topology`` :guilabel:`Topology` int (2x) \n"
"``BondProperty.Type.PeriodicImage`` :guilabel:`Periodic Image` int (3x) \n"
"``BondProperty.Type.Transparency`` :guilabel:`Transparency` float \n"
"======================================================= =================================================== ==========\n"
)
;
......@@ -561,6 +562,7 @@ PYBIND11_MODULE(Particles, m)
.value("Length", BondProperty::LengthProperty)
.value("Topology", BondProperty::TopologyProperty)
.value("PeriodicImage", BondProperty::PeriodicImageProperty)
.value("Transparency", BondProperty::TransparencyProperty)
;
ovito_class<BondType, ElementType>(m,
......
......@@ -232,10 +232,10 @@ bool TachyonRenderer::renderFrame(FrameBuffer* frameBuffer, StereoRenderingTask
// Use only the number of parallel rendering threads allowed by the user.
scene->numthreads = Application::instance()->idealThreadCount();
/* if certain key aspects of the scene parameters have been changed */
/* since the last frame rendered, or when rendering the scene the */
/* first time, various setup, initialization and memory allocation */
/* routines need to be run in order to prepare for rendering. */
// If certain key aspects of the scene parameters have been changed
// since the last frame rendered, or when rendering the scene the
// first time, various setup, initialization and memory allocation
// routines need to be run in order to prepare for rendering.
if(scene->scenecheck)
rendercheck(scene);
......