
.. _custom-datareader-tutorial:

############################################################################
UnstructGrid: Create a Custom DataReader to Add Support for Your File Format
############################################################################

.. image:: ../images/tut_custom_datareader.png
    :height: 300 
    :align: center
    
This tutorial shows how to get your own analysis results into the :class:`cee::ug::UnstructGridModel` by creating a 
:class:`cee::ug::DataReader`.

It is a very simple reader that returns one hexahedron element with one scalar, one vector, one displacement and one 
transformation result, but it will work as a starting point for building your own file reader.

.. note::
    This example expect the application to have a correctly configured :class:`cee::vis::View`
    in place. See demo applications on how to set up a :class:`cee::vis::View` in your application.


**Create the custom DataReader**

To create a custom data reader, you have to sub-class the :class:`cee::ug::DataReader` class. 

First you need to implement a method to detect if the file is supported or not as well as open and close methods. We 
also need to specify how many geometries there are in our file. Usually this is 1.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 38-58

In this case this is really simple as we are doing a sample in-memory "reader"

The next step is to provide information (meta-data) regarding the file that was just opened. To specify the contents of 
the file (number of states, name and id of all the results, etc) you have to implement the 
:func:`cee::ug::DataReader::initializeDirectory()` method. This method will be called by the system after the file is 
successfully opened.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 60-70

Next, we need to provide the geometry. This is done by overriding the 
:func:`readGeometry <cee::ug::DataReader::readGeometry()>` method. In this method, you will have to provide the 
:class:`DataGeometry <cee::ug::DataGeometry>` for the given state and geometry id. 
Please note that if you have a new geometry for each state (adaptive/remeshing model) you have to override the 
:func:`hasNewGeometryForEachState <cee::ug::DataReader::hasNewGeometryForEachState()>` method and return true for the 
geometry/geometries that are remeshed.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 72-96


Next, we provide the scalar result. This method is called whenever the UnstructGridModel (during 
:func:`updateVisualization() <cee::ug::UnstructGridModel::updateVisualization>`) needs a scalar result and it is not 
present in the data source. This could be because the result is specified as fringes result in the 
:class:`ModelSpec <cee::ug::ModelSpec>`, specified to be mapped on a cutting plane or isosurface or specified to be 
mapped on a particle trace. Only the results added to the directory in the 
:func:`initializeDirectory() <cee::ug::DataReader::initializeDirectory>` will be queried by this method.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 98-108


Next, we provide the vector result. This works in the same way as for scalar results.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 110-120


Next, we provide a displacement result. Please note that the displacement results needs to contain new absolute node 
coordinates, not just the displaced value.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 122-140


Finally, we provide a transformation result. This is a rigid body transformation per part.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 142-147


This concludes the custom DataReader and demonstrates how to use all the virtual methods in the interface. 

Please not that the following methods must be overridden (pure virtual methods):

-   :func:`isSupportedFileFormat(const Str& filename) <cee::ug::DataReader::isSupportedFileFormat>`
-   :func:`open(const Str& filename, Error* error) <cee::ug::DataReader::open>`
-   :func:`close() <cee::ug::DataReader::close>`
-   :func:`geometryCount() <cee::ug::DataReader::geometryCount>`
-   :func:`initializeDirectory(DataSourceDirectory* directory, Error* error) <cee::ug::DataReader::initializeDirectory>`
-   :func:`readGeometry(int stateId, size_t geometryIndex, DataGeometry* geometry, Error* error) <cee::ug::DataReader::readGeometry>`

The rest of the methods are optional, but you need to implement read* methods for all the result you provide directory
information about in the :func:`initializeDirectory() <cee::ug::DataReader::initializeDirectory>` method.

Please note that the rest of this example would work with any reader and actually any data source, as it used the 
content from the data source directory to setup the model. The :class:`UnstructGridModel <cee::ug::UnstructGridModel>` 
will use the reader when necessary to load the :class:`DataSource <cee::ug::DataSource>` with the required results. 


**Create model and data source with our custom reader**

Create a model and the :class:`DataSourceReader <cee::ug::DataSourceReader>` data source with our newly created 
DataReader. Then open the "file" so it is ready to use.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 162-167

**Get the available states and results from our reader**

Use the :class:`DataSourceDirectory <cee::ug::DataSourceDirectory>` to get information about states and result provided 
by our reader.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 170-175


**Configure the ModelSpec to show the state and the results**

Here we setup the model spec with the results found in the data source directory. 

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 178-182


**Add the model to the view and update the visualization**

Then we can add the model to the view and call 
:func:`updateVisualization() <cee::ug::UnstructGridModel::updateVisualization>`. This call will trigger loading of the 
results and states specified in the :class:`ModelSpec <cee::ug::ModelSpec>` and our reader will get calls to 
readGeometry(), readScalar() etc. depending on what is specified in the ModelSpec.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 185-189


**Setup the camera and update the viewer**

Finally, as in all other examples, we need to set a default camera position and update the viewer.

.. literalinclude:: ../../../../EnvisionDesktop/Tutorials/CustomDataReader.cpp
    :language: cpp
    :lines: 191-195

**See the complete source code here:**

:ref:`custom-datareader-example`

