UnstructGrid: A simple model with results
This tutorial shows how to build your own UnstructGridModel geometry, part and results. The geometry is a very simple structure containing a single triangle and the following results:
a scalar result mapped as fringes
a vector result
a displacement result
Note
This example expect the application to have a correctly configured cee::vis::View in place. See demo applications on how to set up a cee::vis::View in your application.
Create model and data source
Create a model and set a data source.
Since you will be building our own geometry and not read from a file, we use a
DataSourceMemory object.
Upon creation of the DataSourceMemory, you must specify a unique id for this data
source and how many geometries it will contain. Number of geometries cannot be
changed once the data source has been created. In this example you only have one
geometry.
    cee::PtrRef<cee::ug::UnstructGridModel> ugModel = new cee::ug::UnstructGridModel();
    cee::PtrRef<cee::ug::DataSourceMemory> dataSource = new cee::ug::DataSourceMemory(1, 1);
    ugModel->setDataSource(dataSource.get());
Create state
Each data source can have many data states. This simple model will only have one state. In the construction of a state, you need to give the state an unique id and set how many geometries it contains. The number of geometries for a state must always be the same as for the data source it belongs to!
Create the state with an id and number of geometries and add it to the data source.
The ModelSpec object is the model specification. Among many other settings, the model
specification tells which state(s) are current. Tell the model specification to use this
state.
    int stateId = 1;
    cee::PtrRef<cee::ug::DataState> state = new cee::ug::DataState(stateId, 1);
    dataSource->addState(state.get());
    ugModel->modelSpec().setStateId(stateId);
Create geometry
Create a geometry object and add the geometry to the state at index 0 (since you only have one state).
    int geometryIndex = 0;
    cee::PtrRef<cee::ug::DataGeometry> geo = new cee::ug::DataGeometry;
    state->setGeometry(geometryIndex, geo.get());
Create nodes collection
Create a DataNodes object. DataNodes is a collection of nodes used for building the
triangle. In this example you will use 3 nodes to create a triangle.
Important! The nodes object must be set to the correct size before setting the actual
node coordinates!
    cee::PtrRef<cee::ug::DataNodes> nodes = new cee::ug::DataNodes(false);
    nodes->resize(3);
    nodes->setNode(0, cee::Vec3d(0,0,0));
    nodes->setNode(1, cee::Vec3d(1,0,0));
    nodes->setNode(2, cee::Vec3d(1,1,0));
Create elements
Create the connectivities array for the nodes in the elements. These are specified as one std::vector<unsigned int> for each element. In this example that means two arrays, one for each triangle.
    int c[] = {0, 1, 2};
    std::vector<unsigned int> eltNodes1(c, c+3);    // First triangle
Create the DataElements object. DataElements is a collection of elements used to define
a part. Each element has a element type (here TRIANGLE) and a connectivities array
towards the matching DataNodes object.
Add the two triangle elements.
    cee::PtrRef<cee::ug::DataElements> elements = new cee::ug::DataElements(false, 0);   
    elements->addElement(cee::ug::Element::TRIANGLES, eltNodes1);
Create the part
Create the DataPart object.
A part consists of:
Node coordinates (
DataNodesElement connectivities (
DataElements)
Set the nodes and elements created above into the part.
    int partId = 1;
    cee::PtrRef<cee::ug::DataPart> part = new cee::ug::DataPart(partId);             
    part->setNodes(nodes.get());
    part->setElements(elements.get());
Add the part to the geometry
    geo->addPart(part.get());
Create the scalar result
Create a scalar result using node mapping. The scalar result takes a unique id and the result mapping type upon construction.
    int scalarResultId = 1;
    cee::PtrRef<cee::ug::DataResultScalar> scalarResult = new cee::ug::DataResultScalar(scalarResultId, cee::ug::PER_NODE);
Number of parts in the DataResultScalar must match number of parts in the geometry.
Scalar values and mapping type must also fit the corresponding part in the geometry.
Here the part is a single triangle with three nodes, hence a results array with three
values using node mappings will fit.
Create a scalar result part and add it to the scalar result.
    cee::PtrRef<cee::ug::DataPartScalar> scalarPart = new cee::ug::DataPartScalar();
    scalarPart->resize(3);
    scalarPart->setValue(0, 1.0);
    scalarPart->setValue(1, 2.0);
    scalarPart->setValue(2, 3.0);
    scalarResult->addPart(scalarPart.get());
Each state has a result group for storing available results for each of its geometries. Add the scalar result to the result group for the corresponding geometry.
    state->results(geo.get())->addScalar(scalarResult.get());
Create the vector result
Create a vector result using node mapping. The vector result takes a unique id and the result mapping type upon construction.
    int vectorResultId = 1;
    cee::PtrRef<cee::ug::DataResultVector> vectorResult = new cee::ug::DataResultVector(vectorResultId, cee::ug::PER_NODE);
As for scalars, the number of parts in the DataResultVector must match the number of
parts in the geometry. And result values and mapping type must match the corresponding
part. This means that the vector result array must have three vectors.
    cee::PtrRef<cee::ug::DataPartVector> vectorPart = new cee::ug::DataPartVector();
    vectorPart->resize(3);
    vectorPart->setValue(0, cee::Vec3d(1.0, 0.0, 0.0));
    vectorPart->setValue(1, cee::Vec3d(1.0, 1.0, 0.0));
    vectorPart->setValue(2, cee::Vec3d(0.0, 2.0, 0.0));
    vectorResult->addPart(vectorPart.get());
Add the vector result to the result group for the corresponding geometry.
    state->results(geo.get())->addVector(vectorResult.get());
Create the displacement result
The displacement result takes a unique id upon construction.
    int dispResultId = 1;
    cee::PtrRef<cee::ug::DataResultDisplacement> dispResult = new cee::ug::DataResultDisplacement(dispResultId);
As for scalars and vectors, the number of parts in the DataResultDisplacement must match
the number of parts in the geometry. And result values must match the corresponding
part. This means that the displacement result array must have three displacement vectors.
    cee::PtrRef<cee::ug::DataPartDisplacement> dispPart = new cee::ug::DataPartDisplacement();
    dispPart->resize(3);
    dispPart->setValue(0, cee::Vec3d(1.0, 0.0, 0.0));
    dispPart->setValue(1, cee::Vec3d(0.0, 0.0, 0.0));
    dispPart->setValue(2, cee::Vec3d(0.0, 1.0, 0.0));
    dispResult->addPart(dispPart.get());
Add the displacement result to the result group for the corresponding geometry.
    state->results(geo.get())->addDisplacement(dispResult.get());
Create the transformation result
    cee::PtrRef<cee::ug::DataResultTransformation> transformationResult = new cee::ug::DataResultTransformation();
As for scalars and vectors, the number of parts in the DataResultTransformation must match
the number of parts in the geometry. The part in this case is a transformation matrix which
applies to the part as whole.
    cee::Mat4d partMatrix;
    partMatrix(0,3) = 1.0;
    partMatrix(1,3) = 1.0;
    transformationResult->addPart(partMatrix);
Add the transformation result to the result group for the corresponding geometry.
    state->results(geo.get())->setTransformation(transformationResult.get());
Set up the created model
When you’re done creating the new data source (or have modified it), you need to populate the directory with the latest changes.
    // Set the part info
    cee::ug::PartInfo partInfo(partId, "My part");
    dataSource->directory()->setPartInfo(geometryIndex, partInfo);
    // Set the state info
    cee::ug::StateInfo stateInfo(stateId, "My state", 0.0);
    dataSource->directory()->setStateInfo(stateInfo);
    // Set the scalar result info
    cee::ug::ResultInfo scalarInfo(cee::ug::SCALAR, scalarResultId, "My scalar", cee::ug::PER_NODE);
    dataSource->directory()->setResultInfo(scalarInfo);
    // Set the vector result info
    cee::ug::ResultInfo vectorInfo(cee::ug::VECTOR, vectorResultId, "My vector", cee::ug::PER_NODE);
    dataSource->directory()->setResultInfo(vectorInfo);
    // Set the displacement result info
    cee::ug::ResultInfo displacementInfo(cee::ug::DISPLACEMENT, dispResultId, "My displacement", cee::ug::UNKNOWN_MAPPING);
    dataSource->directory()->setResultInfo(displacementInfo);
    // Set transformation result existence
    dataSource->directory()->setTransformationResult(true);
Then update the model spec to show all results
    // Set modelspec to show all results
    ugModel->modelSpec().setFringesResultId(scalarResultId);
    ugModel->modelSpec().setVectorResultId(vectorResultId);
    ugModel->modelSpec().setDisplacementResultId(dispResultId);
    ugModel->modelSpec().setTransformationResult(true);
    // Set show fringes/vectors/displacement for the part
    ugModel->partSettings(geometryIndex, partId)->setFringesVisible(true);
    ugModel->partSettings(geometryIndex, partId)->setVectorsVisible(true);
    ugModel->partSettings(geometryIndex, partId)->setDisplacementVisible(true);
The model is ready to use and can be added to the view. Exactly where the view exists depends on the platform and solution. These examples uses Qt and the view is set up in a cee::qt::ViewerWidget.
    cee::vis::View* gcView = getTutorialView();
    gcView->addModel(ugModel.get());
    ugModel->updateVisualization();
See the complete source code here: