+ return "cartesian_algo.html#cartesian-hyp-anchor";
+}
+
+//================================================================================
+/*!
+ * \brief Show axes if they are OK
+ */
+//================================================================================
+
+bool StdMeshersGUI_CartesianParamCreator::updateAxesPreview()
+{
+ bool isOk = true;
+ gp_Ax1 axes[3];
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+ for ( int i = 0; i < 3 && isOk; ++i )
+ {
+ gp_XYZ dir( spins[i][0]->GetValue(),
+ spins[i][1]->GetValue(),
+ spins[i][2]->GetValue());
+ if (( isOk = ( dir.Modulus() > 1e-100 )))
+ axes[i].SetDirection( gp_Dir( dir ));
+
+ axes[i].SetLocation ( gp_Pnt( myOrigin[0],
+ myOrigin[1],
+ myOrigin[2]));
+ }
+ gp_Vec norm01 = axes[0].Direction().XYZ() ^ axes[1].Direction().XYZ();
+ gp_Vec norm12 = axes[1].Direction().XYZ() ^ axes[2].Direction().XYZ();
+ if ( isOk )
+ isOk = ( !axes[0].Direction().IsParallel( axes[1].Direction(), theAngTol ) &&
+ !axes[1].Direction().IsParallel( axes[2].Direction(), theAngTol ) &&
+ !axes[2].Direction().IsParallel( axes[0].Direction(), theAngTol ) &&
+ !norm01.IsParallel( norm12, theAngTol ) );
+ if ( isOk )
+ myAxesPreview->SetArrows( axes, myAxesLen );
+
+ myAxesPreview->SetVisibility( isOk );
+
+ return isOk;
+}
+
+//================================================================================
+/*!
+ * \brief Makes axes orthogonal if necessary
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onOrthogonalAxes(bool isOrtho)
+{
+ if ( !isOrtho )
+ {
+ updateAxesPreview();
+ return;
+ }
+
+ std::multimap< int, int > ageOfAxis;
+ gp_XYZ dirs[3];
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+ int nbOk = 0, isOk;
+ for ( int iAx = 0; iAx < 3; ++iAx )
+ {
+ dirs[iAx].SetCoord( spins[iAx][0]->GetValue(),
+ spins[iAx][1]->GetValue(),
+ spins[iAx][2]->GetValue());
+ if (( isOk = ( dirs[iAx].Modulus() > 1e-100 )))
+ ageOfAxis.insert( std::make_pair( myDirTic[iAx], iAx ));
+ else
+ ageOfAxis.insert( std::make_pair( -1, iAx ));
+ nbOk += isOk;
+ }
+ switch ( nbOk )
+ {
+ case 0:
+ {
+ dirs[0].SetCoord( 1, 0, 0 );
+ dirs[1].SetCoord( 0, 1, 0 );
+ dirs[2].SetCoord( 0, 0, 1 );
+ break;
+ }
+ case 1:
+ {
+ int iOk = ageOfAxis.rbegin()->second;
+ get3Dirs( iOk, dirs );
+ break;
+ }
+ default:
+ std::multimap< int, int >::reverse_iterator ag2ax = ageOfAxis.rbegin();
+ int iOk1 = ag2ax->second;
+ int iOk2 = (++ag2ax)->second;
+ int iKo = (++ag2ax)->second;
+ if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
+ std::swap( iOk2, iKo );
+ if ( gp_Vec( dirs[ iOk1 ]).IsParallel( gp_Vec( dirs[ iOk2 ]), theAngTol ))
+ {
+ get3Dirs( iOk1, dirs );
+ }
+ else
+ {
+ dirs[ iKo ] = dirs[ iOk1 ] ^ dirs[ iOk2 ];
+ dirs[ iOk2 ] = dirs[ iKo ] ^ dirs[ iOk1 ];
+ if ( ( iOk1+1 ) % 3 != iOk2 )
+ dirs[ iKo ].Reverse();
+ }
+ }
+
+ for ( int iAx = 0; iAx < 3; ++iAx )
+ {
+ double size = dirs[iAx].Modulus();
+ if ( size > 1e-100 )
+ dirs[iAx] /= size;
+ for (int i = 0; i < 3; ++i )
+ {
+ bool isBlocked = spins[iAx][i]->blockSignals( true );
+ spins[iAx][i]->SetValue( dirs[iAx].Coord( i+1 ));
+ spins[iAx][i]->blockSignals( isBlocked );
+ }
+ }
+
+ updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief Increment myDirTic and update the preview of axes
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onAxisDirChange(const QString&)
+{
+ QObject* changedSpin = sender();
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+ for ( int iAx = 0; iAx < 3; ++iAx )
+ if ( spins[iAx][0] == changedSpin ||
+ spins[iAx][1] == changedSpin ||
+ spins[iAx][2] == changedSpin )
+ {
+ myDirTic[ iAx ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
+ break;
+ }
+
+ onOrthogonalAxes( myOrthogonalChk->isChecked() );
+}
+
+//================================================================================
+/*!
+ * \brief Sets axis direction by a selected EDGE
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onSelectionChange()
+{
+ int iAxis = myAxisBtnGrp->checkedId();
+ if ( iAxis < 0 )
+ return;
+
+ SALOME_ListIO aList;
+ SMESHGUI::GetSMESHGUI()->selectionMgr()->selectedObjects(aList);
+
+ TopoDS_Shape edge, shape;
+ for( SALOME_ListIteratorOfListIO anIt( aList ); anIt.More(); anIt.Next() )
+ {
+ GEOM::GEOM_Object_var go = SMESH::IObjectToInterface<GEOM::GEOM_Object>( anIt.Value() );
+ if ( GEOMBase::GetShape( go, shape ) && shape.ShapeType() == TopAbs_EDGE )
+ {
+ if ( !edge.IsNull() )
+ return; // several EDGEs selected
+ edge = shape;
+ }
+ }
+ if ( edge.IsNull() )
+ return;
+
+ TopoDS_Shape vv[2];
+ TopoDS_Iterator vIt( edge );
+ for ( ; vIt.More() && vv[1].IsNull(); vIt.Next() )
+ vv[ !vv[0].IsNull() ] = vIt.Value();
+
+ gp_Pnt pp[2];
+ if ( !GEOMBase::VertexToPoint( vv[0], pp[0] ) ||
+ !GEOMBase::VertexToPoint( vv[1], pp[1] ))
+ return;
+
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+
+ gp_Vec newDir( pp[0], pp[1] );
+ gp_Vec curDir( spins[iAxis][0]->GetValue(),
+ spins[iAxis][1]->GetValue(),
+ spins[iAxis][2]->GetValue());
+ if ( newDir * curDir < 0 )
+ newDir.Reverse();
+
+ double size = newDir.Magnitude();
+ if ( size < 1e-100 )
+ return;
+ newDir /= size;
+
+ for (int i = 0; i < 3; ++i )
+ {
+ bool isBlocked = spins[iAxis][i]->blockSignals( true );
+ spins[iAxis][i]->SetValue( newDir.Coord( i+1 ));
+ spins[iAxis][i]->blockSignals( isBlocked );
+ }
+ myDirTic[ iAxis ] = 1 + Max( Max( myDirTic[0], myDirTic[1] ), myDirTic[2] );
+
+ onOrthogonalAxes( myOrthogonalChk->isChecked() );
+}
+
+//================================================================================
+/*!
+ * \brief Sets axes at which number of hexahedra is maximal
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onOptimalAxes(bool)
+{
+ StdMeshers::StdMeshers_CartesianParameters3D_var h =
+ StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hypothesis() );
+ if ( h->_is_nil() )
+ return;
+
+ QString shapeEntry = getMainShapeEntry();
+ if ( shapeEntry.isEmpty() )
+ return;
+
+ GEOM::GEOM_Object_var geomObj = SMESH::EntryToInterface<GEOM::GEOM_Object>( shapeEntry );
+ if ( geomObj->_is_nil() )
+ return;
+
+ SMESH::DirStruct axDirs[3];
+ h->ComputeOptimalAxesDirs( geomObj,
+ myOrthogonalChk->isChecked(),
+ axDirs[0],
+ axDirs[1],
+ axDirs[2]);
+
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+ for ( int iAx = 0; iAx < 3; ++iAx )
+ {
+ double coords[3] = { axDirs[iAx].PS.x, axDirs[iAx].PS.y, axDirs[iAx].PS.z };
+ for (int i = 0; i < 3; ++i )
+ {
+ bool isBlocked = spins[iAx][i]->blockSignals( true );
+ spins[iAx][i]->SetValue( coords[ i ]);
+ spins[iAx][i]->blockSignals( isBlocked );
+ }
+ }
+ updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief Sets axes || to the axes of global CS
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onResetAxes(bool)
+{
+ SMESHGUI_SpinBox** spins[3] = { &myXDirSpin[0], &myYDirSpin[0], &myZDirSpin[0] };
+ for ( int iAx = 0; iAx < 3; ++iAx )
+ {
+ for (int i = 0; i < 3; ++i )
+ {
+ bool isBlocked = spins[iAx][i]->blockSignals( true );
+ spins[iAx][i]->SetValue( iAx == i ? 1. : 0. );
+ spins[iAx][i]->blockSignals( isBlocked );
+ }
+ myDirTic[iAx] = 0;
+ }
+ updateAxesPreview();
+}
+
+//================================================================================
+/*!
+ * \brief SLOT called when the grid definition mode changes
+ */
+//================================================================================
+
+void StdMeshersGUI_CartesianParamCreator::onGridModeChanged(int)
+{
+ bool haveSpacing = ( myAxisTabs[0]->isGridBySpacing() ||
+ myAxisTabs[1]->isGridBySpacing() ||
+ myAxisTabs[2]->isGridBySpacing() );
+
+ myFixedPointGrp->setEnabled( haveSpacing );