Skip to content
Snippets Groups Projects
fieldVisualisationBase.C 16.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
        \\  /    A nd           | Copyright (C) 2015 OpenFOAM Foundation
    
         \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    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
        <
            functionObjects::fieldVisualisationBase::colourByType,
            2
        >::names[] =
    
        const char* NamedEnum
        <
            functionObjects::fieldVisualisationBase::colourMapType,
            4
        >::names[] =
    
    const Foam::NamedEnum
        <
            Foam::functionObjects::fieldVisualisationBase::colourByType,
            2
        >
        Foam::functionObjects::fieldVisualisationBase::colourByTypeNames;
    
    const Foam::NamedEnum
        <
            Foam::functionObjects::fieldVisualisationBase::colourMapType,
            4
        >
        Foam::functionObjects::fieldVisualisationBase::colourMapTypeNames;
    
    
    
    // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
    
    
    void Foam::functionObjects::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::functionObjects::fieldVisualisationBase::addScalarBar
    
    (
        const scalar position,
        vtkRenderer* renderer,
        vtkLookupTable* lut
    ) const
    {
    
        if (!scalarBar_.visible_)
        {
            return;
        }
    
        vtkSmartPointer<vtkScalarBarActor> sbar =
            vtkSmartPointer<vtkScalarBarActor>::New();
        sbar->SetLookupTable(lut);
        sbar->SetNumberOfLabels(scalarBar_.numberOfLabels_);
    
        const vector textColour = colours_["text"]->value(position);
    
    
        // Work-around to supply our own scalarbar title
    
        // - Default scalar bar title text is scales by the scalar bar box
        //   dimensions so if the title is a long string, the text is shrunk to fit
        //   Instead, suppress title and set the title using a vtkTextActor
    
        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();
    
    
        // How to use the standard scalar bar text
        // 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->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::functionObjects::fieldVisualisationBase::setField
    
    (
        const scalar position,
        const word& colourFieldName,
        vtkPolyDataMapper* mapper,
    
        vtkRenderer* renderer,
        vtkPolyData* pData
    
    ) const
    {
        mapper->InterpolateScalarsBeforeMappingOn();
    
        switch (colourBy_)
        {
            case cbColour:
            {
                mapper->ScalarVisibilityOff();
                break;
            }
            case cbField:
            {
    
                vtkSmartPointer<vtkLookupTable> lut =
                    vtkSmartPointer<vtkLookupTable>::New();
                setColourMap(lut);
                lut->SetVectorMode(vtkScalarsToColors::MAGNITUDE);
    
    
                const char* fieldName = colourFieldName.c_str();
                mapper->SelectColorArray(fieldName);
    
                // Set to use either point or cell data
                // Note: if both point and cell data exists, preferentially
                //       choosing point data.  This is often the case when using
                //       glyphs
                if (pData->GetPointData()->HasArray(fieldName) == 1)
                {
                    mapper->SetScalarModeToUsePointFieldData();
                }
                else if (pData->GetCellData()->HasArray(fieldName) == 1)
                {
                    mapper->SetScalarModeToUseCellFieldData();
                }
                else
                {
                    WarningInFunction
                        << "Unable to determine cell or point data type "
                        << "- assuming point data";
                    mapper->SetScalarModeToUsePointFieldData();
                }
    
                mapper->SetScalarRange(range_.first(), range_.second());
                mapper->SetColorModeToMapScalars();
                mapper->SetLookupTable(lut);
                mapper->ScalarVisibilityOn();
    
    
                addScalarBar(position, renderer, lut);
                break;
            }
        }
    
        mapper->Modified();
    }
    
    
    
    
    void Foam::functionObjects::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;
    
    
        // Determine whether we have scalar or vector data
        label nComponents = -1;
        const char* scaleFieldNameChar = scaleFieldName.c_str();
        if (data->GetPointData()->HasArray(scaleFieldNameChar) == 1)
        {
            nComponents =
                data->GetPointData()->GetArray(scaleFieldNameChar)
                    ->GetNumberOfComponents();
        }
        else if (data->GetCellData()->HasArray(scaleFieldNameChar) == 1)
        {
            // Need to convert cell data to point data
            vtkSmartPointer<vtkCellDataToPointData> cellToPoint =
                vtkSmartPointer<vtkCellDataToPointData>::New();
            cellToPoint->SetInputData(data);
            cellToPoint->Update();
            vtkDataSet* pds = cellToPoint->GetOutput();
            vtkDataArray* pData = pds->GetPointData()->GetArray(scaleFieldNameChar);
    
            // Store in main vtkPolyData
            data->GetPointData()->AddArray(pData);
    
            nComponents = pData->GetNumberOfComponents();
        }
        else
        {
            WarningInFunction
                << "Glyphs can only be added to scalar or vector data. "
                << "Unable to process field " << scaleFieldName << endl;
            return;
        }
    
    
        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(scaleFieldNameChar);
                // values->GetRange(range);
    
                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,
    
            );
        }
        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 =
    
                double range[6];
                values->GetRange(range);
    
    
                // 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,
    
                << "Glyphs can only be added to scalar and vector fields."
    
                << " Field " << scaleFieldName << " has " << nComponents
                << " components" << endl;
    
            ok = false;
        }
    
        if (ok)
        {
            glyph->Update();
    
    
            setField(position, colourFieldName, glyphMapper, renderer, data);
    
    
            glyphMapper->Update();
    
            actor->SetMapper(glyphMapper);
    
            renderer->AddActor(actor);
        }
    }
    
    
    // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
    
    
    Foam::functionObjects::fieldVisualisationBase::fieldVisualisationBase
    
        const HashPtrTable<Function1<vector>, word>& colours
    
        colourBy_(cbColour),
        colourMap_(cmRainbow),
        range_()
    {
        colourBy_ = colourByTypeNames.read(dict.lookup("colourBy"));
    
        switch (colourBy_)
        {
            case cbColour:
            {
    
                break;
            }
            case cbField:
            {
                dict.lookup("range") >> range_;
    
    
                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_;
                }
    
                break;
            }
        }
    }
    
    
    // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
    
    
    Foam::functionObjects::fieldVisualisationBase::~fieldVisualisationBase()
    
    {}
    
    
    // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
    
    
    const Foam::HashPtrTable<Foam::Function1<Foam::vector>, Foam::word>&
    
    Foam::functionObjects::fieldVisualisationBase::colours() const
    
    const Foam::word&
    Foam::functionObjects::fieldVisualisationBase::fieldName() const
    
    {
        return fieldName_;
    }
    
    
    // ************************************************************************* //