diff --git a/src/OBJECT/SMESH_Object.cxx b/src/OBJECT/SMESH_Object.cxx index 58715e251..1566d3506 100644 --- a/src/OBJECT/SMESH_Object.cxx +++ b/src/OBJECT/SMESH_Object.cxx @@ -134,6 +134,7 @@ SMESH_VisualObjDef::SMESH_VisualObjDef() { MESSAGE("---------------------------------------------SMESH_VisualObjDef::SMESH_VisualObjDef"); myGrid = vtkUnstructuredGrid::New(); + myLocalGrid = false; } SMESH_VisualObjDef::~SMESH_VisualObjDef() { @@ -149,29 +150,41 @@ SMESH_VisualObjDef::~SMESH_VisualObjDef() //================================================================================= vtkIdType SMESH_VisualObjDef::GetNodeObjId( int theVTKID ) { -// TMapOfIds::const_iterator i = myVTK2SMDSNodes.find(theVTKID); -// return i == myVTK2SMDSNodes.end() ? -1 : i->second; + if (myLocalGrid) + { + TMapOfIds::const_iterator i = myVTK2SMDSNodes.find(theVTKID); + return i == myVTK2SMDSNodes.end() ? -1 : i->second; + } return theVTKID; } vtkIdType SMESH_VisualObjDef::GetNodeVTKId( int theObjID ) { -// TMapOfIds::const_iterator i = mySMDS2VTKNodes.find(theObjID); -// return i == mySMDS2VTKNodes.end() ? -1 : i->second; + if (myLocalGrid) + { + TMapOfIds::const_iterator i = mySMDS2VTKNodes.find(theObjID); + return i == mySMDS2VTKNodes.end() ? -1 : i->second; + } return theObjID; } vtkIdType SMESH_VisualObjDef::GetElemObjId( int theVTKID ) { -// TMapOfIds::const_iterator i = myVTK2SMDSElems.find(theVTKID); -// return i == myVTK2SMDSElems.end() ? -1 : i->second; + if (myLocalGrid) + { + TMapOfIds::const_iterator i = myVTK2SMDSElems.find(theVTKID); + return i == myVTK2SMDSElems.end() ? -1 : i->second; + } return this->GetMesh()->fromVtkToSmds(theVTKID); } vtkIdType SMESH_VisualObjDef::GetElemVTKId( int theObjID ) { -// TMapOfIds::const_iterator i = mySMDS2VTKElems.find(theObjID); -// return i == mySMDS2VTKElems.end() ? -1 : i->second; + if (myLocalGrid) + { + TMapOfIds::const_iterator i = mySMDS2VTKElems.find(theObjID); + return i == mySMDS2VTKElems.end() ? -1 : i->second; + } return this->GetMesh()->fromSmdsToVtk(theObjID); } @@ -179,72 +192,83 @@ vtkIdType SMESH_VisualObjDef::GetElemVTKId( int theObjID ) // function : SMESH_VisualObjDef::createPoints // purpose : Create points from nodes //================================================================================= +/*! fills a vtkPoints structure for a submesh. + * fills a std::list of SMDS_MeshElements*, then extract the points. + * fills also conversion id maps between SMDS and VTK. + */ +void SMESH_VisualObjDef::createPoints( vtkPoints* thePoints ) +{ + if ( thePoints == 0 ) + return; -//void SMESH_VisualObjDef::createPoints( vtkPoints* thePoints ) -//{ -// if ( thePoints == 0 ) -// return; -// -// TEntityList aNodes; -// vtkIdType nbNodes = GetEntities( SMDSAbs_Node, aNodes ); -// thePoints->SetNumberOfPoints( nbNodes ); -// -// int nbPoints = 0; -// -// TEntityList::const_iterator anIter; -// for ( anIter = aNodes.begin(); anIter != aNodes.end(); ++anIter ) -// { -// const SMDS_MeshNode* aNode = ( const SMDS_MeshNode* )(*anIter); -// if ( aNode != 0 ) -// { -// thePoints->SetPoint( nbPoints, aNode->X(), aNode->Y(), aNode->Z() ); -// int anId = aNode->GetID(); -// mySMDS2VTKNodes.insert( TMapOfIds::value_type( anId, nbPoints ) ); -// myVTK2SMDSNodes.insert( TMapOfIds::value_type( nbPoints, anId ) ); -// nbPoints++; -// } -// } -// -// if ( nbPoints != nbNodes ) -// thePoints->SetNumberOfPoints( nbPoints ); -//} + TEntityList aNodes; + vtkIdType nbNodes = GetEntities( SMDSAbs_Node, aNodes ); + thePoints->SetNumberOfPoints( nbNodes ); + + int nbPoints = 0; + + TEntityList::const_iterator anIter; + for ( anIter = aNodes.begin(); anIter != aNodes.end(); ++anIter ) + { + const SMDS_MeshNode* aNode = ( const SMDS_MeshNode* )(*anIter); + if ( aNode != 0 ) + { + thePoints->SetPoint( nbPoints, aNode->X(), aNode->Y(), aNode->Z() ); + int anId = aNode->GetID(); + mySMDS2VTKNodes.insert( TMapOfIds::value_type( anId, nbPoints ) ); + myVTK2SMDSNodes.insert( TMapOfIds::value_type( nbPoints, anId ) ); + nbPoints++; + } + } + + if ( nbPoints != nbNodes ) + thePoints->SetNumberOfPoints( nbPoints ); +} //================================================================================= // function : buildPrs // purpose : create VTK cells( fill unstructured grid ) //================================================================================= -void SMESH_VisualObjDef::buildPrs() +void SMESH_VisualObjDef::buildPrs(bool buildGrid) { -// try -// { -// mySMDS2VTKNodes.clear(); -// myVTK2SMDSNodes.clear(); -// mySMDS2VTKElems.clear(); -// myVTK2SMDSElems.clear(); -// -// if ( IsNodePrs() ) -// buildNodePrs(); -// else -// buildElemPrs(); -// } -// catch(...) -// { -// mySMDS2VTKNodes.clear(); -// myVTK2SMDSNodes.clear(); -// mySMDS2VTKElems.clear(); -// myVTK2SMDSElems.clear(); -// -// myGrid->SetPoints( 0 ); -// myGrid->SetCells( 0, 0, 0 ); -// throw; -// } - MESSAGE("----------------------------------------------------------SMESH_VisualObjDef::buildPrs"); - vtkUnstructuredGrid *theGrid = GetMesh()->getGrid(); - myGrid->ShallowCopy(theGrid); - MESSAGE(myGrid->GetReferenceCount()); - MESSAGE( "Update - myGrid->GetNumberOfCells() = "<GetNumberOfCells() ); - MESSAGE( "Update - myGrid->GetNumberOfPoints() = "<GetNumberOfPoints() ); - if( MYDEBUGWITHFILES ) SMESH::WriteUnstructuredGrid( myGrid,"/tmp/buildPrs" ); + MESSAGE("----------------------------------------------------------SMESH_VisualObjDef::buildPrs " << buildGrid); + if (buildGrid) + { + myLocalGrid = true; + try + { + mySMDS2VTKNodes.clear(); + myVTK2SMDSNodes.clear(); + mySMDS2VTKElems.clear(); + myVTK2SMDSElems.clear(); + + if ( IsNodePrs() ) + buildNodePrs(); + else + buildElemPrs(); + } + catch(...) + { + mySMDS2VTKNodes.clear(); + myVTK2SMDSNodes.clear(); + mySMDS2VTKElems.clear(); + myVTK2SMDSElems.clear(); + + myGrid->SetPoints( 0 ); + myGrid->SetCells( 0, 0, 0 ); + throw; + } + } + else + { + myLocalGrid = false; + vtkUnstructuredGrid *theGrid = GetMesh()->getGrid(); + myGrid->ShallowCopy(theGrid); + MESSAGE(myGrid->GetReferenceCount()); + MESSAGE( "Update - myGrid->GetNumberOfCells() = "<GetNumberOfCells() ); + MESSAGE( "Update - myGrid->GetNumberOfPoints() = "<GetNumberOfPoints() ); + if( MYDEBUGWITHFILES ) SMESH::WriteUnstructuredGrid( myGrid,"/tmp/buildPrs" ); + } } //================================================================================= @@ -252,19 +276,19 @@ void SMESH_VisualObjDef::buildPrs() // purpose : create VTK cells for nodes //================================================================================= -//void SMESH_VisualObjDef::buildNodePrs() -//{ -// // PAL16631: without swap, bad_alloc is not thrown but hung up and crash instead, -// // so check remaining memory size for safety -// SMDS_Mesh::CheckMemory(); // PAL16631 -// vtkPoints* aPoints = vtkPoints::New(); -// createPoints( aPoints ); -// SMDS_Mesh::CheckMemory(); -// myGrid->SetPoints( aPoints ); -// aPoints->Delete(); -// -// myGrid->SetCells( 0, 0, 0 ); -//} +void SMESH_VisualObjDef::buildNodePrs() +{ + // PAL16631: without swap, bad_alloc is not thrown but hung up and crash instead, + // so check remaining memory size for safety + // SMDS_Mesh::CheckMemory(); // PAL16631 + vtkPoints* aPoints = vtkPoints::New(); + createPoints( aPoints ); + // SMDS_Mesh::CheckMemory(); + myGrid->SetPoints( aPoints ); + aPoints->Delete(); + + myGrid->SetCells( 0, 0, 0 ); +} //================================================================================= // function : buildElemPrs @@ -296,199 +320,199 @@ namespace{ } -//void SMESH_VisualObjDef::buildElemPrs() -//{ -// // Create points -// -// vtkPoints* aPoints = vtkPoints::New(); -// createPoints( aPoints ); -// myGrid->SetPoints( aPoints ); -// aPoints->Delete(); -// -// if ( MYDEBUG ) -// MESSAGE("Update - myGrid->GetNumberOfPoints() = "<GetNumberOfPoints()); -// -// // Calculate cells size -// -// static SMDSAbs_ElementType aTypes[ 4 ] = -// { SMDSAbs_0DElement, SMDSAbs_Edge, SMDSAbs_Face, SMDSAbs_Volume }; -// -// // get entity data -// map nbEnts; -// map anEnts; -// -// for ( int i = 0; i <= 3; i++ ) -// nbEnts[ aTypes[ i ] ] = GetEntities( aTypes[ i ], anEnts[ aTypes[ i ] ] ); -// -// // PAL16631: without swap, bad_alloc is not thrown but hung up and crash instead, -// // so check remaining memory size for safety -// SMDS_Mesh::CheckMemory(); // PAL16631 -// -// vtkIdType aCellsSize = 2 * nbEnts[ SMDSAbs_0DElement ] + 3 * nbEnts[ SMDSAbs_Edge ]; -// -// for ( int i = 2; i <= 3; i++ ) // iterate through faces and volumes -// { -// if ( nbEnts[ aTypes[ i ] ] ) -// { -// const TEntityList& aList = anEnts[ aTypes[ i ] ]; -// TEntityList::const_iterator anIter; -// for ( anIter = aList.begin(); anIter != aList.end(); ++anIter ) -// aCellsSize += (*anIter)->NbNodes() + 1; -// } -// } -// -// vtkIdType aNbCells = nbEnts[ SMDSAbs_0DElement ] + nbEnts[ SMDSAbs_Edge ] + -// nbEnts[ SMDSAbs_Face ] + nbEnts[ SMDSAbs_Volume ]; -// -// if ( MYDEBUG ) -// MESSAGE( "Update - aNbCells = "<X()<<","<Y()<<","<Z()<<")"< 0) { -// for (vtkIdType aNodeId = 0; aNodeId < aNbNodes; aNodeId++) -// SetId(anIdList,mySMDS2VTKNodes,aConnect,aNodeId,aConnectivities[aNodeId]); -// } -// break; -// } -// default: -// for( vtkIdType aNodeId = 0; aNodesIter->more(); aNodeId++ ){ -// const SMDS_MeshElement* aNode = aNodesIter->next(); -// anIdList->SetId( aNodeId, mySMDS2VTKNodes[aNode->GetID()] ); -// } -// } -// -// aConnectivity->InsertNextCell( anIdList ); -// aCellTypesArray->InsertNextValue( getCellType( aType, anElem->IsPoly(), aNbNodes ) ); -// -// iElem++; -// } -// } -// SMDS_Mesh::CheckMemory(); // PAL16631 -// } -// -// // Insert cells in grid -// -// VTKViewer_CellLocationsArray* aCellLocationsArray = VTKViewer_CellLocationsArray::New(); -// aCellLocationsArray->SetNumberOfComponents( 1 ); -// aCellLocationsArray->SetNumberOfTuples( aNbCells ); -// -// SMDS_Mesh::CheckMemory(); // PAL16631 -// -// aConnectivity->InitTraversal(); -// for( vtkIdType idType = 0, *pts, npts; aConnectivity->GetNextCell( npts, pts ); idType++ ) -// aCellLocationsArray->SetValue( idType, aConnectivity->GetTraversalLocation( npts ) ); -// -// myGrid->SetCells( aCellTypesArray, aCellLocationsArray,aConnectivity ); -// -// aCellLocationsArray->Delete(); -// aCellTypesArray->Delete(); -// aConnectivity->Delete(); -// anIdList->Delete(); -// -// SMDS_Mesh::CheckMemory(); // PAL16631 -//} +void SMESH_VisualObjDef::buildElemPrs() +{ + // Create points + + vtkPoints* aPoints = vtkPoints::New(); + createPoints( aPoints ); + myGrid->SetPoints( aPoints ); + aPoints->Delete(); + + if ( MYDEBUG ) + MESSAGE("Update - myGrid->GetNumberOfPoints() = "<GetNumberOfPoints()); + + // Calculate cells size + + static SMDSAbs_ElementType aTypes[ 4 ] = + { SMDSAbs_0DElement, SMDSAbs_Edge, SMDSAbs_Face, SMDSAbs_Volume }; + + // get entity data + map nbEnts; + map anEnts; + + for ( int i = 0; i <= 3; i++ ) + nbEnts[ aTypes[ i ] ] = GetEntities( aTypes[ i ], anEnts[ aTypes[ i ] ] ); + + // PAL16631: without swap, bad_alloc is not thrown but hung up and crash instead, + // so check remaining memory size for safety + // SMDS_Mesh::CheckMemory(); // PAL16631 + + vtkIdType aCellsSize = 2 * nbEnts[ SMDSAbs_0DElement ] + 3 * nbEnts[ SMDSAbs_Edge ]; + + for ( int i = 2; i <= 3; i++ ) // iterate through faces and volumes + { + if ( nbEnts[ aTypes[ i ] ] ) + { + const TEntityList& aList = anEnts[ aTypes[ i ] ]; + TEntityList::const_iterator anIter; + for ( anIter = aList.begin(); anIter != aList.end(); ++anIter ) + aCellsSize += (*anIter)->NbNodes() + 1; + } + } + + vtkIdType aNbCells = nbEnts[ SMDSAbs_0DElement ] + nbEnts[ SMDSAbs_Edge ] + + nbEnts[ SMDSAbs_Face ] + nbEnts[ SMDSAbs_Volume ]; + + if ( MYDEBUG ) + MESSAGE( "Update - aNbCells = "<X()<<","<Y()<<","<Z()<<")"< 0) { + for (vtkIdType aNodeId = 0; aNodeId < aNbNodes; aNodeId++) + SetId(anIdList,mySMDS2VTKNodes,aConnect,aNodeId,aConnectivities[aNodeId]); + } + break; + } + default: + for( vtkIdType aNodeId = 0; aNodesIter->more(); aNodeId++ ){ + const SMDS_MeshElement* aNode = aNodesIter->next(); + anIdList->SetId( aNodeId, mySMDS2VTKNodes[aNode->GetID()] ); + } + } + + aConnectivity->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( getCellType( aType, anElem->IsPoly(), aNbNodes ) ); + + iElem++; + } + } + // SMDS_Mesh::CheckMemory(); // PAL16631 + } + + // Insert cells in grid + + VTKViewer_CellLocationsArray* aCellLocationsArray = VTKViewer_CellLocationsArray::New(); + aCellLocationsArray->SetNumberOfComponents( 1 ); + aCellLocationsArray->SetNumberOfTuples( aNbCells ); + + // SMDS_Mesh::CheckMemory(); // PAL16631 + + aConnectivity->InitTraversal(); + for( vtkIdType idType = 0, *pts, npts; aConnectivity->GetNextCell( npts, pts ); idType++ ) + aCellLocationsArray->SetValue( idType, aConnectivity->GetTraversalLocation( npts ) ); + + myGrid->SetCells( aCellTypesArray, aCellLocationsArray,aConnectivity ); + + aCellLocationsArray->Delete(); + aCellTypesArray->Delete(); + aConnectivity->Delete(); + anIdList->Delete(); + + // SMDS_Mesh::CheckMemory(); // PAL16631 +} //================================================================================= // function : GetEdgeNodes @@ -587,7 +611,7 @@ SMESH_MeshObj::~SMESH_MeshObj() bool SMESH_MeshObj::Update( int theIsClear ) { // Update SMDS_Mesh on client part - MESSAGE("SMESH_MeshObj::Update"); + MESSAGE("SMESH_MeshObj::Update " << this); if ( myClient.Update(theIsClear) || GetUnstructuredGrid()->GetNumberOfPoints()==0) { buildPrs(); // Fill unstructured grid return true; @@ -777,8 +801,9 @@ void SMESH_SubMeshObj::UpdateFunctor( const SMESH::Controls::FunctorPtr& theFunc //================================================================================= bool SMESH_SubMeshObj::Update( int theIsClear ) { + MESSAGE("SMESH_SubMeshObj::Update " << this) bool changed = myMeshObj->Update( theIsClear ); - buildPrs(); + buildPrs(true); return changed; } diff --git a/src/OBJECT/SMESH_ObjectDef.h b/src/OBJECT/SMESH_ObjectDef.h index ab79b9a01..f136c3714 100644 --- a/src/OBJECT/SMESH_ObjectDef.h +++ b/src/OBJECT/SMESH_ObjectDef.h @@ -77,7 +77,7 @@ public: int& theNodeId1, int& theNodeId2 ) const; - virtual vtkUnstructuredGrid* GetUnstructuredGrid(); // { return myGrid; } + virtual vtkUnstructuredGrid* GetUnstructuredGrid(); virtual vtkIdType GetNodeObjId( int theVTKID ); virtual vtkIdType GetNodeVTKId( int theObjID ); @@ -86,17 +86,18 @@ public: protected: -// void createPoints( vtkPoints* ); - void buildPrs(); -// void buildNodePrs(); -// void buildElemPrs(); + void createPoints( vtkPoints* ); + void buildPrs(bool buildGrid = false); + void buildNodePrs(); + void buildElemPrs(); //private: -// TMapOfIds mySMDS2VTKNodes; -// TMapOfIds myVTK2SMDSNodes; -// TMapOfIds mySMDS2VTKElems; -// TMapOfIds myVTK2SMDSElems; + TMapOfIds mySMDS2VTKNodes; + TMapOfIds myVTK2SMDSNodes; + TMapOfIds mySMDS2VTKElems; + TMapOfIds myVTK2SMDSElems; + bool myLocalGrid; vtkUnstructuredGrid* myGrid; }; diff --git a/src/SMDS/Notes b/src/SMDS/Notes index 38ac69ae1..0cdb92abb 100644 --- a/src/SMDS/Notes +++ b/src/SMDS/Notes @@ -1,10 +1,14 @@ Problemes en cours ================== +- a faire ++ en cours, OK mais perfectible +* OK -- visualisation de groupe (type d'element): on voit tout le maillage, mais le groupe est OK ++ visualisation de groupe (type d'element): on voit tout le maillage, mais le groupe est OK + creation d'une structure vtkUnstructuredGrid locale : iteration un peu lourde, et pas de partage avec la structure du maillage (pas evident) - inversion d'un volume (tetra): exception - script de creation de noeuds et d'elements: OK, mais pas compatible avec version precedente (numerotation noeuds differente) -- affichage numeros noeuds: numeros en trop sur O (enlever dans vtkUnstructuredGrid) +- affichage numeros noeuds: numeros en trop sur (O,0,0) pas systematique, trouver la condition (enlever dans vtkUnstructuredGrid ?) A tester, non pris en compte ============================ diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 10e2d8820..53d8b7373 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -1538,7 +1538,10 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) aSel->selectedObjects( sel_objects ); if( theCommandID==302 ) + { + MESSAGE("anAction = SMESH::eDisplayOnly"); startOperation( myEraseAll ); + } extractContainers( sel_objects, to_process ); @@ -1549,20 +1552,26 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if (vtkwnd) { SALOME_ListIteratorOfListIO It( to_process ); for ( ; It.More(); It.Next()) { + MESSAGE("---"); Handle(SALOME_InteractiveObject) IOS = It.Value(); if (IOS->hasEntry()) { + MESSAGE("---"); if (!SMESH::UpdateView(anAction, IOS->getEntry())) { SMESHGUI::GetSMESHGUI()->EmitSignalVisibilityChanged(); break; // PAL16774 (Crash after display of many groups) } if (anAction == SMESH::eDisplayOnly) + { + MESSAGE("anAction = SMESH::eDisplayOnly"); anAction = SMESH::eDisplay; + } } } } // PAL13338 + PAL15161 --> if ( ( theCommandID==301 || theCommandID==302 ) && !checkLock(aStudy)) { + MESSAGE("anAction = SMESH::eDisplayOnly"); SMESH::UpdateView(); SMESHGUI::GetSMESHGUI()->EmitSignalVisibilityChanged(); } @@ -1573,6 +1582,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) } if (anAction == SMESH::eErase) { + MESSAGE("anAction == SMESH::eErase"); SALOME_ListIO l1; aSel->setSelectedObjects( l1 ); } diff --git a/src/SMESHGUI/SMESHGUI_VTKUtils.cxx b/src/SMESHGUI/SMESHGUI_VTKUtils.cxx index 97577596f..61d21573a 100644 --- a/src/SMESHGUI/SMESHGUI_VTKUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_VTKUtils.cxx @@ -603,6 +603,7 @@ namespace SMESH } } } + MESSAGE("CreateActor " << anActor); return anActor; } @@ -613,6 +614,7 @@ namespace SMESH #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 OCC_CATCH_SIGNALS; #endif + MESSAGE("DisplayActor " << theActor); vtkWnd->AddActor(theActor); vtkWnd->Repaint(); } @@ -628,6 +630,7 @@ namespace SMESH void RemoveActor( SUIT_ViewWindow *theWnd, SMESH_Actor* theActor){ if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(theWnd)){ + MESSAGE("RemoveActor " << theActor); vtkWnd->RemoveActor(theActor); if(theActor->hasIO()){ Handle(SALOME_InteractiveObject) anIO = theActor->getIO(); @@ -683,6 +686,7 @@ namespace SMESH case eDisplayAll: { while (vtkActor *anAct = aCollection->GetNextActor()) { if (SMESH_Actor *anActor = dynamic_cast(anAct)) { + MESSAGE("--- display " << anActor); anActor->SetVisibility(true); } } @@ -690,8 +694,10 @@ namespace SMESH } case eDisplayOnly: case eEraseAll: { + MESSAGE("---case eDisplayOnly"); while (vtkActor *anAct = aCollection->GetNextActor()) { if (SMESH_Actor *anActor = dynamic_cast(anAct)) { + MESSAGE("--- erase " << anActor); anActor->SetVisibility(false); } } @@ -701,10 +707,12 @@ namespace SMESH switch (theAction) { case eDisplay: case eDisplayOnly: + MESSAGE("--- display " << anActor); anActor->SetVisibility(true); if (theAction == eDisplayOnly) aRenderer->ResetCameraClippingRange(); break; case eErase: + MESSAGE("--- erase " << anActor); anActor->SetVisibility(false); break; } @@ -713,6 +721,7 @@ namespace SMESH case eDisplay: case eDisplayOnly: { + MESSAGE("---"); SalomeApp_Study* aStudy = dynamic_cast(theWnd->getViewManager()->study()); _PTR(Study) aDocument = aStudy->studyDS(); // Pass non-visual objects (hypotheses, etc.), return true in this case