+//================================================================================
+/*!
+ * \brief Setup quadPoints
+ */
+//================================================================================
+
+_Mapper2D::_Mapper2D( const TParam2ColumnMap & param2ColumnMap, const TNode2Edge& n2eMap )
+{
+ size_t i, iSize = _quadPoints.iSize = param2ColumnMap.size();
+ size_t j, jSize = _quadPoints.jSize = param2ColumnMap.begin()->second.size();
+ if ( _quadPoints.iSize < 3 ||
+ _quadPoints.jSize < 3 )
+ return;
+ _quadPoints.uv_grid.resize( iSize * jSize );
+
+ // set nodes
+ i = 0;
+ for ( auto & u_columnNodes : param2ColumnMap )
+ {
+ for ( j = 0; j < u_columnNodes.second.size(); ++j )
+ _quadPoints.UVPt( i, j ).node = u_columnNodes.second[ j ];
+ ++i;
+ }
+
+ // compute x parameter on borders
+ uvPnt( 0, 0 ).x = 0;
+ uvPnt( 0, jSize-1 ).x = 0;
+ gp_Pnt p0, pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0 ).node );
+ gp_Pnt p1, pPrev1 = SMESH_NodeXYZ( uvPnt( 0, jSize-1 ).node );
+ for ( i = 1; i < iSize; ++i )
+ {
+ p0 = SMESH_NodeXYZ( uvPnt( i, 0 ).node );
+ p1 = SMESH_NodeXYZ( uvPnt( i, jSize-1 ).node );
+ uvPnt( i, 0 ).x = uvPnt( i-1, 0 ).x + p0.Distance( pPrev0 );
+ uvPnt( i, jSize-1 ).x = uvPnt( i-1, jSize-1 ).x + p1.Distance( pPrev1 );
+ pPrev0 = p0;
+ pPrev1 = p1;
+ }
+ for ( i = 1; i < iSize-1; ++i )
+ {
+ uvPnt( i, 0 ).x /= uvPnt( iSize-1, 0 ).x;
+ uvPnt( i, jSize-1 ).x /= uvPnt( iSize-1, jSize-1 ).x;
+ uvPnt( i, 0 ).y = 0;
+ uvPnt( i, jSize-1 ).y = 1;
+ }
+
+ // compute y parameter on borders
+ uvPnt( 0, 0 ).y = 0;
+ uvPnt( iSize-1, 0 ).y = 0;
+ pPrev0 = SMESH_NodeXYZ( uvPnt( 0, 0 ).node );
+ pPrev1 = SMESH_NodeXYZ( uvPnt( iSize-1, 0 ).node );
+ for ( j = 1; j < jSize; ++j )
+ {
+ p0 = SMESH_NodeXYZ( uvPnt( 0, j ).node );
+ p1 = SMESH_NodeXYZ( uvPnt( iSize-1, j ).node );
+ uvPnt( 0, j ).y = uvPnt( 0, j-1 ).y + p0.Distance( pPrev0 );
+ uvPnt( iSize-1, j ).y = uvPnt( iSize-1, j-1 ).y + p1.Distance( pPrev1 );
+ pPrev0 = p0;
+ pPrev1 = p1;
+ }
+ for ( j = 1; j < jSize-1; ++j )
+ {
+ uvPnt( 0, j ).y /= uvPnt( 0, jSize-1 ).y;
+ uvPnt( iSize-1, j ).y /= uvPnt( iSize-1, jSize-1 ).y;
+ uvPnt( 0, j ).x = 0;
+ uvPnt( iSize-1, j ).x = 1;
+ }
+
+ // compute xy of internal nodes
+ for ( i = 1; i < iSize-1; ++i )
+ {
+ const double x0 = uvPnt( i, 0 ).x;
+ const double x1 = uvPnt( i, jSize-1 ).x;
+ for ( j = 1; j < jSize-1; ++j )
+ {
+ const double y0 = uvPnt( 0, j ).y;
+ const double y1 = uvPnt( iSize-1, j ).y;
+ double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0));
+ double y = y0 + x * (y1 - y0);
+ uvPnt( i, j ).x = x;
+ uvPnt( i, j ).y = y;
+ }
+ }
+
+ // replace base nodes with target ones
+ for ( i = 0; i < iSize; ++i )
+ for ( j = 0; j < jSize; ++j )
+ {
+ auto n2e = n2eMap.find( uvPnt( i, j ).node );
+ uvPnt( i, j ).node = n2e->second->_nodes.back();
+ }
+
+ return;
+}
+
+//================================================================================
+/*!
+ * \brief Compute positions of nodes of 2D structured mesh using TFI
+ */
+//================================================================================
+
+bool _Mapper2D::ComputeNodePositions()
+{
+ if ( _quadPoints.uv_grid.empty() )
+ return true;
+
+ size_t i, iSize = _quadPoints.iSize;
+ size_t j, jSize = _quadPoints.jSize;
+
+ SMESH_NodeXYZ a0 ( uvPnt( 0, 0 ).node );
+ SMESH_NodeXYZ a1 ( uvPnt( iSize-1, 0 ).node );
+ SMESH_NodeXYZ a2 ( uvPnt( iSize-1, jSize-1 ).node );
+ SMESH_NodeXYZ a3 ( uvPnt( 0, jSize-1 ).node );
+
+ for ( i = 1; i < iSize-1; ++i )
+ {
+ SMESH_NodeXYZ p0 ( uvPnt( i, 0 ).node );
+ SMESH_NodeXYZ p2 ( uvPnt( i, jSize-1 ).node );
+ for ( j = 1; j < jSize-1; ++j )
+ {
+ SMESH_NodeXYZ p1 ( uvPnt( iSize-1, j ).node );
+ SMESH_NodeXYZ p3 ( uvPnt( 0, j ).node );
+ double x = uvPnt( i, j ).x;
+ double y = uvPnt( i, j ).y;
+
+ gp_XYZ p = SMESH_MesherHelper::calcTFI( x, y, a0,a1,a2,a3, p0,p1,p2,p3 );
+ const_cast< SMDS_MeshNode* >( uvPnt( i, j ).node )->setXYZ( p.X(), p.Y(), p.Z() );
+
+ dumpMove( uvPnt( i, j ).node );
+ }
+ }
+ return true;
+}
+