Files
openfoam/src/postProcessing/functionObjects/graphics/runTimePostProcessing/fieldVisualisationBase.C
Andrew Heather 40680a43b1 ENH: Initial commit of new runTimePostProcessing function object
- Allows generation of images (currently PNG files) during the run
- ... or afterwards by invoking the execFlowFunctionObjects utility
- Wrapper around VTK functionality
- Support for objects:
  - text
  - points (glyphs: sphere, arrow)
  - lines (tubes)
  - surfaces (wireframe, shaded, combination)
- Colour using:
  - user-defined
  - field values (several colour maps availale)
- For image sequences:
  - dynamic views (camera movement)
  - objects can appear/disappear using opacity
- Building
  - VTK dependency v6+
  - satisfied using ParaView from ThirdParty directory
  - or separate VTK installation
2015-11-11 12:55:15 +00:00

518 lines
14 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2015 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// OpenFOAM includes
#include "fieldVisualisationBase.H"
#include "runTimePostProcessing.H"
// VTK includes
#include "vtkArrowSource.h"
#include "vtkColorTransferFunction.h"
#include "vtkFloatArray.h"
#include "vtkGlyph3D.h"
#include "vtkLookupTable.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkRenderer.h"
#include "vtkScalarBarActor.h"
#include "vtkSmartPointer.h"
#include "vtkSphereSource.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
namespace Foam
{
template<>
const char* NamedEnum<fieldVisualisationBase::colourByType, 2>::names[] =
{
"colour",
"field"
};
template<>
const char* NamedEnum<fieldVisualisationBase::colourMapType, 4>::names[] =
{
"rainbow",
"blueWhiteRed",
"fire",
"greyscale"
};
}
const Foam::NamedEnum<Foam::fieldVisualisationBase::colourByType, 2>
Foam::fieldVisualisationBase::colourByTypeNames;
const Foam::NamedEnum<Foam::fieldVisualisationBase::colourMapType, 4>
Foam::fieldVisualisationBase::colourMapTypeNames;
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
void Foam::fieldVisualisationBase::setColourMap(vtkLookupTable* lut) const
{
label nColours = 256;
lut->SetNumberOfColors(nColours);
vtkSmartPointer<vtkColorTransferFunction> ctf =
vtkSmartPointer<vtkColorTransferFunction>::New();
switch (colourMap_)
{
case cmRainbow:
{
ctf->SetColorSpaceToHSV();
ctf->AddRGBPoint(0, 0, 0, 1);
ctf->AddRGBPoint(0.5, 0, 1, 0);
ctf->AddRGBPoint(1, 1, 0, 0);
break;
}
case cmBlueWhiteRed:
{
// Values taken from ParaView settings
ctf->SetColorSpaceToDiverging();
ctf->AddRGBPoint(0.0, 0.231373, 0.298039, 0.752941);
ctf->AddRGBPoint(0.5, 0.865003, 0.865003, 0.865003);
ctf->AddRGBPoint(1.0, 0.705882, 0.0156863, 0.14902);
break;
}
case cmFire:
{
// Values taken from ParaView settings
ctf->SetColorSpaceToRGB();
ctf->AddRGBPoint(0, 0, 0, 0);
ctf->AddRGBPoint(0.4, 0.901961, 0, 0);
ctf->AddRGBPoint(0.8, 0.901961, 0.901961, 0);
ctf->AddRGBPoint(1, 1, 1, 1);
break;
}
case cmGreyscale:
{
ctf->SetColorSpaceToRGB();
ctf->AddRGBPoint(0, 0, 0, 0);
ctf->AddRGBPoint(1, 1, 1, 1);
break;
}
}
for (label i = 0; i < nColours; i++)
{
double* c = ctf->GetColor(scalar(i)/scalar(nColours));
lut->SetTableValue(i, c[0], c[1], c[2], 1.0);
}
}
void Foam::fieldVisualisationBase::addScalarBar
(
const scalar position,
vtkRenderer* renderer,
vtkLookupTable* lut
) const
{
// add scalar bar legend
if (!scalarBar_.visible_)
{
return;
}
vtkSmartPointer<vtkScalarBarActor> sbar =
vtkSmartPointer<vtkScalarBarActor>::New();
sbar->SetLookupTable(lut);
sbar->SetNumberOfLabels(scalarBar_.numberOfLabels_);
const vector textColour = colours_["text"]->value(position);
// workaround to supply our own scalarbar title
vtkSmartPointer<vtkTextActor> titleActor =
vtkSmartPointer<vtkTextActor>::New();
sbar->SetTitle(" ");
titleActor->SetInput(scalarBar_.title_.c_str());
titleActor->GetTextProperty()->SetFontFamilyToArial();
titleActor->GetTextProperty()->SetFontSize(3*scalarBar_.fontSize_);
titleActor->GetTextProperty()->SetJustificationToCentered();
titleActor->GetTextProperty()->SetVerticalJustificationToBottom();
titleActor->GetTextProperty()->BoldOn();
titleActor->GetTextProperty()->ItalicOff();
titleActor->GetTextProperty()->SetColor
(
textColour[0],
textColour[1],
textColour[2]
);
titleActor->GetPositionCoordinate()->
SetCoordinateSystemToNormalizedViewport();
/*
sbar->SetTitle(scalarBar_.title_.c_str());
sbar->GetTitleTextProperty()->SetColor
(
textColour[0],
textColour[1],
textColour[2]
);
sbar->GetTitleTextProperty()->SetFontSize(scalarBar_.fontSize_);
sbar->GetTitleTextProperty()->ShadowOff();
sbar->GetTitleTextProperty()->BoldOn();
sbar->GetTitleTextProperty()->ItalicOff();
*/
sbar->GetLabelTextProperty()->SetColor
(
textColour[0],
textColour[1],
textColour[2]
);
sbar->GetLabelTextProperty()->SetFontSize(scalarBar_.fontSize_);
sbar->GetLabelTextProperty()->ShadowOff();
sbar->GetLabelTextProperty()->BoldOff();
sbar->GetLabelTextProperty()->ItalicOff();
sbar->SetLabelFormat(scalarBar_.labelFormat_.c_str());
sbar->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
sbar->GetPositionCoordinate()->SetValue
(
scalarBar_.position_.first(),
scalarBar_.position_.second()
);
if (scalarBar_.vertical_)
{
sbar->SetOrientationToVertical();
sbar->SetWidth(0.1);
sbar->SetHeight(0.75);
sbar->SetTextPositionToSucceedScalarBar();
}
else
{
sbar->SetOrientationToHorizontal();
// adjustments since not using scalarbar title property
sbar->SetWidth(0.75);
sbar->SetHeight(0.07);
sbar->SetBarRatio(0.5);
// sbar->SetHeight(0.1);
// sbar->SetTitleRatio(0.01);
sbar->SetTextPositionToPrecedeScalarBar();
}
titleActor->GetPositionCoordinate()->SetValue
(
scalarBar_.position_.first() + 0.5*sbar->GetWidth(),
scalarBar_.position_.second() + sbar->GetHeight()
);
// sbar->DrawFrameOn();
// sbar->DrawBackgroundOn();
// sbar->UseOpacityOff();
// sbar->VisibilityOff();
sbar->VisibilityOn();
renderer->AddActor(sbar);
renderer->AddActor2D(titleActor);
}
void Foam::fieldVisualisationBase::setField
(
const scalar position,
const word& colourFieldName,
vtkPolyDataMapper* mapper,
vtkRenderer* renderer
) const
{
mapper->InterpolateScalarsBeforeMappingOn();
switch (colourBy_)
{
case cbColour:
{
mapper->ScalarVisibilityOff();
break;
}
case cbField:
{
// create look-up table for colours
vtkSmartPointer<vtkLookupTable> lut =
vtkSmartPointer<vtkLookupTable>::New();
setColourMap(lut);
lut->SetVectorMode(vtkScalarsToColors::MAGNITUDE);
// configure the mapper
mapper->SelectColorArray(colourFieldName.c_str());
mapper->SetScalarRange(range_.first(), range_.second());
mapper->SetScalarModeToUsePointFieldData();
mapper->SetColorModeToMapScalars();
mapper->SetLookupTable(lut);
mapper->ScalarVisibilityOn();
// add the bar
addScalarBar(position, renderer, lut);
break;
}
}
mapper->Modified();
}
void Foam::fieldVisualisationBase::addGlyphs
(
const scalar position,
const word& scaleFieldName,
const word& colourFieldName,
const scalar maxGlyphLength,
vtkPolyData* data,
vtkActor* actor,
vtkRenderer* renderer
) const
{
vtkSmartPointer<vtkGlyph3D> glyph = vtkSmartPointer<vtkGlyph3D>::New();
vtkSmartPointer<vtkPolyDataMapper> glyphMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
glyphMapper->SetInputConnection(glyph->GetOutputPort());
glyph->SetInputData(data);
glyph->ScalingOn();
bool ok = true;
label nComponents =
data->GetPointData()->GetArray(scaleFieldName.c_str())
->GetNumberOfComponents();
if (nComponents == 1)
{
vtkSmartPointer<vtkSphereSource> sphere =
vtkSmartPointer<vtkSphereSource>::New();
sphere->SetCenter(0, 0, 0);
sphere->SetRadius(0.5);
// setting higher resolution slows the rendering significantly
// sphere->SetPhiResolution(20);
// sphere->SetThetaResolution(20);
glyph->SetSourceConnection(sphere->GetOutputPort());
if (maxGlyphLength > 0)
{
double range[2];
// can use values to find range
// vtkDataArray* values =
// data->GetPointData()->GetScalars(scaleFieldName.c_str());
// values->GetRange(range);
// set range accoding to user-supplied limits
range[0] = range_.first();
range[1] = range_.second();
glyph->ClampingOn();
glyph->SetRange(range);
// if range[0] != min(value), maxGlyphLength behaviour will not
// be correct...
glyph->SetScaleFactor(maxGlyphLength);
}
else
{
glyph->SetScaleFactor(1);
}
glyph->SetScaleModeToScaleByScalar();
glyph->OrientOff();
glyph->SetColorModeToColorByScalar();
glyph->SetInputArrayToProcess
(
0, // scalars
0,
0,
vtkDataObject::FIELD_ASSOCIATION_POINTS,
scaleFieldName.c_str()
);
}
else if (nComponents == 3)
{
vtkSmartPointer<vtkArrowSource> arrow =
vtkSmartPointer<vtkArrowSource>::New();
arrow->SetTipResolution(10);
arrow->SetTipRadius(0.1);
arrow->SetTipLength(0.35);
arrow->SetShaftResolution(10);
arrow->SetShaftRadius(0.03);
glyph->SetSourceConnection(arrow->GetOutputPort());
if (maxGlyphLength > 0)
{
vtkDataArray* values =
data->GetPointData()->GetVectors(scaleFieldName.c_str());
double range[6];
values->GetRange(range);
/*
// attempt to set range for vectors...
scalar x0 = sqrt(sqr(range_.first())/3.0);
scalar x1 = sqrt(sqr(range_.second())/3.0);
range[0] = x0;
range[1] = x0;
range[2] = x0;
range[3] = x1;
range[4] = x1;
range[5] = x1;
*/
glyph->ClampingOn();
glyph->SetRange(range);
glyph->SetScaleFactor(maxGlyphLength);
}
else
{
glyph->SetScaleFactor(1);
}
glyph->SetScaleModeToScaleByVector();
glyph->OrientOn();
glyph->SetVectorModeToUseVector();
glyph->SetColorModeToColorByVector();
glyph->SetInputArrayToProcess
(
1, // vectors
0,
0,
vtkDataObject::FIELD_ASSOCIATION_POINTS,
scaleFieldName.c_str()
);
}
else
{
WarningIn
(
"void Foam::fieldVisualisationBase::addGlyphs"
"("
"const scalar, "
"const word&, "
"const word&, "
"const scalar, "
"vtkPolyData*, "
"vtkActor*, "
"vtkRenderer*"
") const"
)
<< "Glyphs can only be added to " << pTraits<scalar>::typeName
<< " and " << pTraits<vector>::typeName << " fields. "
<< " Field " << scaleFieldName << " has " << nComponents
<< " components" << endl;
ok = false;
}
if (ok)
{
glyph->Update();
setField(position, colourFieldName, glyphMapper, renderer);
glyphMapper->Update();
actor->SetMapper(glyphMapper);
renderer->AddActor(actor);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fieldVisualisationBase::fieldVisualisationBase
(
const runTimePostProcessing& parent,
const dictionary& dict,
const HashPtrTable<DataEntry<vector>, word>& colours
)
:
parent_(parent),
colours_(colours),
fieldName_(dict.lookup("fieldName")),
colourBy_(cbColour),
colourMap_(cmRainbow),
range_()
{
colourBy_ = colourByTypeNames.read(dict.lookup("colourBy"));
switch (colourBy_)
{
case cbColour:
{
break;
}
case cbField:
{
dict.lookup("range") >> range_;
break;
}
}
if (dict.found("colourMap"))
{
colourMap_ = colourMapTypeNames.read(dict.lookup("colourMap"));
}
const dictionary& sbarDict = dict.subDict("scalarBar");
sbarDict.lookup("visible") >> scalarBar_.visible_;
if (scalarBar_.visible_)
{
sbarDict.lookup("vertical") >> scalarBar_.vertical_;
sbarDict.lookup("position") >> scalarBar_.position_;
sbarDict.lookup("title") >> scalarBar_.title_;
sbarDict.lookup("fontSize") >> scalarBar_.fontSize_;
sbarDict.lookup("labelFormat") >> scalarBar_.labelFormat_;
sbarDict.lookup("numberOfLabels") >> scalarBar_.numberOfLabels_;
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::fieldVisualisationBase::~fieldVisualisationBase()
{}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
const Foam::HashPtrTable<Foam::DataEntry<Foam::vector>, Foam::word>&
Foam::fieldVisualisationBase::colours() const
{
return colours_;
}
const Foam::word& Foam::fieldVisualisationBase::fieldName() const
{
return fieldName_;
}
// ************************************************************************* //