From d833925587cce97912651657d5dc7e60d0110330 Mon Sep 17 00:00:00 2001 From: vpa Date: Mon, 31 Aug 2015 10:32:00 +0300 Subject: [PATCH] 0026462: Visualization - selection does not adapt to line width change - added interface for changing sensitivity of a particular selection through both local and interactive context; - added corresponding methods for changing sensitivity to SelectMgr_SelectionManager, SelectMgr_Selection and SelectBasics_SensitiveEntity; - option -setSensitivity was implemented in vaspects Draw command; - test case for issue #26462 --- src/AIS/AIS_InteractiveContext.cdl | 10 +++- src/AIS/AIS_InteractiveContext.cxx | 17 ++++++ src/AIS/AIS_LocalContext.cdl | 8 +++ src/AIS/AIS_LocalContext.cxx | 11 ++++ src/QABugs/QABugs_19.cxx | 58 +++++++++++++++++++ .../SelectBasics_SensitiveEntity.cdl | 12 ++-- .../SelectBasics_SensitiveEntity.cxx | 12 ++++ .../SelectBasics_SensitiveEntity.lxx | 9 --- src/SelectMgr/SelectMgr_Selection.cxx | 31 +++++++++- src/SelectMgr/SelectMgr_Selection.hxx | 6 ++ src/SelectMgr/SelectMgr_SelectionManager.cdl | 9 ++- src/SelectMgr/SelectMgr_SelectionManager.cxx | 48 +++++++++++++++ src/ViewerTest/ViewerTest.cxx | 49 +++++++++++++++- tests/bugs/vis/bug26462_1 | 53 +++++++++++++++++ tests/bugs/vis/bug26462_2 | 15 +++++ 15 files changed, 326 insertions(+), 22 deletions(-) create mode 100644 tests/bugs/vis/bug26462_1 create mode 100644 tests/bugs/vis/bug26462_2 diff --git a/src/AIS/AIS_InteractiveContext.cdl b/src/AIS/AIS_InteractiveContext.cdl index 66094f7ee7..f75a954ee8 100644 --- a/src/AIS/AIS_InteractiveContext.cdl +++ b/src/AIS/AIS_InteractiveContext.cdl @@ -442,7 +442,15 @@ is ---Purpose: Returns the pixel tolerance. ---Category: put locations on objects.... - -- + -- + + SetSelectionSensitivity (me : mutable; + theObject : InteractiveObject from AIS; + theMode : Integer from Standard; + theNewSensitivity : Integer from Standard); + ---Purpose: Allows to manage sensitivity of a particular selection of interactive object theObject and + -- changes previous sensitivity value of all sensitive entities in selection with theMode to + -- the given theNewSensitivity. SetLocation(me:mutable; aniobj : InteractiveObject from AIS; diff --git a/src/AIS/AIS_InteractiveContext.cxx b/src/AIS/AIS_InteractiveContext.cxx index 24df744e62..a0a1556dc5 100644 --- a/src/AIS/AIS_InteractiveContext.cxx +++ b/src/AIS/AIS_InteractiveContext.cxx @@ -2622,6 +2622,23 @@ Standard_Integer AIS_InteractiveContext::PixelTolerance() const : myMainSel->PixelTolerance(); } +//======================================================================= +//function : SetSelectionSensitivity +//purpose : Allows to manage sensitivity of a particular selection of interactive object theObject +//======================================================================= +void AIS_InteractiveContext::SetSelectionSensitivity (const Handle(AIS_InteractiveObject)& theObject, + const Standard_Integer theMode, + const Standard_Integer theNewSensitivity) +{ + if (HasOpenedContext()) + { + myLocalContexts (myCurLocalIndex)->SetSelectionSensitivity (theObject, theMode, theNewSensitivity); + return; + } + + mgrSelector->SetSelectionSensitivity (theObject, theMode, theNewSensitivity); +} + //======================================================================= //function : IsInLocal //purpose : diff --git a/src/AIS/AIS_LocalContext.cdl b/src/AIS/AIS_LocalContext.cdl index f123a8693c..ac7a3b9344 100644 --- a/src/AIS/AIS_LocalContext.cdl +++ b/src/AIS/AIS_LocalContext.cdl @@ -447,6 +447,14 @@ is ---Level: Public ---Purpose: Returns the pixel tolerance. + SetSelectionSensitivity (me : mutable; + theObject : InteractiveObject from AIS; + theMode : Integer from Standard; + theNewSensitivity : Integer from Standard); + ---Purpose: Allows to manage sensitivity of a particular selection of interactive object theObject and + -- changes previous sensitivity value of all sensitive entities in selection with theMode to + -- the given theNewSensitivity. + ---Category: IMMEDIATE MODE diff --git a/src/AIS/AIS_LocalContext.cxx b/src/AIS/AIS_LocalContext.cxx index b810c92178..c8f94e84d2 100644 --- a/src/AIS/AIS_LocalContext.cxx +++ b/src/AIS/AIS_LocalContext.cxx @@ -1149,3 +1149,14 @@ Standard_Integer AIS_LocalContext::PixelTolerance() const { return myMainVS->PixelTolerance(); } + +//======================================================================= +//function : SetSelectionSensitivity +//purpose : Allows to manage sensitivity of a particular selection of interactive object theObject +//======================================================================= +void AIS_LocalContext::SetSelectionSensitivity (const Handle(AIS_InteractiveObject)& theObject, + const Standard_Integer theMode, + const Standard_Integer theNewSensitivity) +{ + mySM->SetSelectionSensitivity (theObject, theMode, theNewSensitivity); +} diff --git a/src/QABugs/QABugs_19.cxx b/src/QABugs/QABugs_19.cxx index be55212a2a..0120428d64 100755 --- a/src/QABugs/QABugs_19.cxx +++ b/src/QABugs/QABugs_19.cxx @@ -4207,6 +4207,61 @@ static Standard_Integer OCC26195 (Draw_Interpretor& theDI, Standard_Integer theA return 0; } +//======================================================================= +//function : OCC26462 +//purpose : +//======================================================================= +static Standard_Integer OCC26462 (Draw_Interpretor& theDI, Standard_Integer /*theArgNb*/, const char** /*theArgVec*/) +{ + if (ViewerTest::GetAISContext().IsNull()) + { + std::cerr << "Error: No opened context!\n"; + return 1; + } + + BRepPrimAPI_MakeBox aBuilder1 (gp_Pnt (10.0, 10.0, 0.0), 10.0, 10.0, 10.0); + BRepPrimAPI_MakeBox aBuilder2 (10.0, 10.0, 10.0); + Handle(AIS_InteractiveObject) aBox1 = new AIS_Shape (aBuilder1.Shape()); + Handle(AIS_InteractiveObject) aBox2 = new AIS_Shape (aBuilder2.Shape()); + + const Handle(AIS_InteractiveContext) aCtx = ViewerTest::GetAISContext(); + aCtx->OpenLocalContext(); + aCtx->Display (aBox1, 0, 2); + aCtx->Display (aBox2, 0, 2); + ViewerTest::CurrentView()->FitAll(); + aCtx->SetWidth (aBox1, 3); + aCtx->SetWidth (aBox2, 3); + + aCtx->MoveTo (305, 322, ViewerTest::CurrentView()); + aCtx->ShiftSelect(); + aCtx->MoveTo (103, 322, ViewerTest::CurrentView()); + aCtx->ShiftSelect(); + if (aCtx->NbSelected() != 0) + { + theDI << "ERROR: no boxes must be selected!\n"; + return 1; + } + + aCtx->SetSelectionSensitivity (aBox1, 2, 5); + + aCtx->MoveTo (305, 322, ViewerTest::CurrentView()); + aCtx->ShiftSelect(); + if (aCtx->NbSelected() != 1) + { + theDI << "ERROR: b1 was not selected\n"; + return 1; + } + aCtx->MoveTo (103, 322, ViewerTest::CurrentView()); + aCtx->ShiftSelect(); + if (aCtx->NbSelected() != 1) + { + theDI << "ERROR: b2 is selected after b1's tolerance increased\n"; + return 1; + } + + return 0; +} + void QABugs::Commands_19(Draw_Interpretor& theCommands) { const char *group = "QABugs"; @@ -4292,5 +4347,8 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) { "\n\t\t: [toPrintPixelCoord 0|1] - prints 3d projection of pixel coordinate or center of" "\n\t\t: selecting rectangle onto near and far view frustum planes", __FILE__, OCC26195, group); + theCommands.Add ("OCC26462", + "OCC26462: Checks the ability to manage sensitivity of a particular selection mode in local context", + __FILE__, OCC26462, group); return; } diff --git a/src/SelectBasics/SelectBasics_SensitiveEntity.cdl b/src/SelectBasics/SelectBasics_SensitiveEntity.cdl index c31e6b8a8b..34a64c9cc1 100644 --- a/src/SelectBasics/SelectBasics_SensitiveEntity.cdl +++ b/src/SelectBasics/SelectBasics_SensitiveEntity.cdl @@ -46,6 +46,12 @@ is ---C++: return const& ---Purpose: Returns pointer to owner of the entity + SetSensitivityFactor (me : mutable; + theSensFactor : Integer from Standard) + is static; + ---Level: Public + ---Purpose: Allows to manage the sensitivity of the entity + Matches (me : mutable; theMgr : out SelectingVolumeManager from SelectBasics; @@ -91,12 +97,6 @@ is ---Purpose: Returns inversed location transformation matrix if the shape corresponding -- to this entity has init location set. Otherwise, returns identity matrix. - SetSensitivityFactor (me : mutable; - theSensFactor : Integer from Standard) - is protected; - ---C++: inline - ---Purpose: Allows to manage the sensitivity of the entity - fields diff --git a/src/SelectBasics/SelectBasics_SensitiveEntity.cxx b/src/SelectBasics/SelectBasics_SensitiveEntity.cxx index b04aa82066..cf7dd65ed8 100644 --- a/src/SelectBasics/SelectBasics_SensitiveEntity.cxx +++ b/src/SelectBasics/SelectBasics_SensitiveEntity.cxx @@ -43,3 +43,15 @@ const Handle(SelectBasics_EntityOwner)& SelectBasics_SensitiveEntity::OwnerId() { return myOwnerId; } + +//======================================================================= +// function : SetSensitivityFactor +// purpose : Allows to manage sensitivity of a particular entity +//======================================================================= +void SelectBasics_SensitiveEntity::SetSensitivityFactor (const Standard_Integer theNewSens) +{ + Standard_ASSERT_RAISE (theNewSens > 0, + "Error! Selection sensitivity have positive value."); + + mySFactor = theNewSens; +} diff --git a/src/SelectBasics/SelectBasics_SensitiveEntity.lxx b/src/SelectBasics/SelectBasics_SensitiveEntity.lxx index 380ceb17fc..9d134fecc1 100644 --- a/src/SelectBasics/SelectBasics_SensitiveEntity.lxx +++ b/src/SelectBasics/SelectBasics_SensitiveEntity.lxx @@ -12,15 +12,6 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. -//======================================================================= -// function : SetSensitivityFactor -// purpose : Allows to manage the sensitivity of the entity -//======================================================================= -inline void SelectBasics_SensitiveEntity::SetSensitivityFactor (const Standard_Integer theSensFactor) -{ - mySFactor = theSensFactor; -} - //======================================================================= // function : SensitivityFactor // purpose : Gets sensitivity factor for the entity diff --git a/src/SelectMgr/SelectMgr_Selection.cxx b/src/SelectMgr/SelectMgr_Selection.cxx index 4bfb2a8dd0..5eb8c0d7bb 100644 --- a/src/SelectMgr/SelectMgr_Selection.cxx +++ b/src/SelectMgr/SelectMgr_Selection.cxx @@ -29,7 +29,8 @@ SelectMgr_Selection::SelectMgr_Selection (const Standard_Integer theModeIdx) : myMode (theModeIdx), mySelectionState (SelectMgr_SOS_Unknown), myBVHUpdateStatus (SelectMgr_TBU_None), - mySensFactor (2) + mySensFactor (2), + myIsCustomSens (Standard_False) {} SelectMgr_Selection::~SelectMgr_Selection() @@ -73,8 +74,15 @@ void SelectMgr_Selection::Add (const Handle(SelectBasics_SensitiveEntity)& theSe anEntity->SetActiveForSelection(); } - mySensFactor = Max (mySensFactor, - anEntity->BaseSensitive()->SensitivityFactor()); + if (myIsCustomSens) + { + anEntity->BaseSensitive()->SetSensitivityFactor (mySensFactor); + } + else + { + mySensFactor = Max (mySensFactor, + anEntity->BaseSensitive()->SensitivityFactor()); + } } } @@ -138,3 +146,20 @@ Standard_Integer SelectMgr_Selection::Sensitivity() const { return mySensFactor; } + +//================================================== +// function: SetSensitivity +// purpose : Changes sensitivity of the selection and all its entities to the given value. +// IMPORTANT: This method does not update any outer selection structures, so for +// proper updates use SelectMgr_SelectionManager::SetSelectionSensitivity method. +//================================================== +void SelectMgr_Selection::SetSensitivity (const Standard_Integer theNewSens) +{ + mySensFactor = theNewSens; + myIsCustomSens = Standard_True; + for (Standard_Integer anIdx = 0; anIdx < myEntities.Size(); ++anIdx) + { + Handle(SelectMgr_SensitiveEntity)& anEntity = myEntities.ChangeValue (anIdx); + anEntity->BaseSensitive()->SetSensitivityFactor (theNewSens); + } +} diff --git a/src/SelectMgr/SelectMgr_Selection.hxx b/src/SelectMgr/SelectMgr_Selection.hxx index 1536b68a36..7a48b25bff 100644 --- a/src/SelectMgr/SelectMgr_Selection.hxx +++ b/src/SelectMgr/SelectMgr_Selection.hxx @@ -133,6 +133,11 @@ public: //! Returns sensitivity of the selection Standard_EXPORT Standard_Integer Sensitivity() const; + //! Changes sensitivity of the selection and all its entities to the given value. + //! IMPORTANT: This method does not update any outer selection structures, so for + //! proper updates use SelectMgr_SelectionManager::SetSelectionSensitivity method. + Standard_EXPORT void SetSensitivity (const Standard_Integer theNewSens); + DEFINE_STANDARD_RTTI (SelectMgr_Selection) protected: @@ -149,6 +154,7 @@ private: mutable SelectMgr_StateOfSelection mySelectionState; mutable SelectMgr_TypeOfBVHUpdate myBVHUpdateStatus; Standard_Integer mySensFactor; + Standard_Boolean myIsCustomSens; }; DEFINE_STANDARD_HANDLE(SelectMgr_Selection, MMgt_TShared) diff --git a/src/SelectMgr/SelectMgr_SelectionManager.cdl b/src/SelectMgr/SelectMgr_SelectionManager.cdl index 157d671a44..27ba83700f 100644 --- a/src/SelectMgr/SelectMgr_SelectionManager.cdl +++ b/src/SelectMgr/SelectMgr_SelectionManager.cdl @@ -201,7 +201,14 @@ is is static; ---Purpose: Sets type of update of selection with theMode of theObject to the given theType. - + SetSelectionSensitivity (me : mutable; + theObject : SelectableObject from SelectMgr; + theMode : Integer from Standard; + theNewSens : Integer from Standard) + is static; + ---Purpose: Allows to manage sensitivity of a particular selection of interactive object theObject and + -- changes previous sensitivity value of all sensitive entities in selection with theMode + -- to the given theNewSensitivity. diff --git a/src/SelectMgr/SelectMgr_SelectionManager.cxx b/src/SelectMgr/SelectMgr_SelectionManager.cxx index 88ff77cbd2..d2a1012dbd 100644 --- a/src/SelectMgr/SelectMgr_SelectionManager.cxx +++ b/src/SelectMgr/SelectMgr_SelectionManager.cxx @@ -903,3 +903,51 @@ void SelectMgr_SelectionManager::SetUpdateMode (const Handle(SelectMgr_Selectabl theObject->Selection (theMode)->UpdateStatus (theType); } +//======================================================================= +//function : SetSelectionSensitivity +//purpose : Allows to manage sensitivity of a particular selection of interactive object theObject and +// changes previous sensitivity value of all sensitive entities in selection with theMode +// to the given theNewSensitivity. +//======================================================================= +void SelectMgr_SelectionManager::SetSelectionSensitivity (const Handle(SelectMgr_SelectableObject)& theObject, + const Standard_Integer theMode, + const Standard_Integer theNewSens) +{ + Standard_ASSERT_RAISE (theNewSens > 0, + "Error! Selection sensitivity have positive value."); + + if (theObject.IsNull() || !theObject->HasSelection (theMode)) + return; + + Handle(SelectMgr_Selection) aSel = theObject->Selection (theMode); + const Standard_Integer aPrevSens = aSel->Sensitivity(); + aSel->SetSensitivity (theNewSens); + + if (!(myGlobal.Contains (theObject) || myLocal.IsBound (theObject))) + return; + +if (myGlobal.Contains (theObject)) + { + for (TColStd_MapIteratorOfMapOfTransient aSelectorsIter (mySelectors); aSelectorsIter.More(); aSelectorsIter.Next()) + { + Handle(SelectMgr_ViewerSelector) aSelector = Handle(SelectMgr_ViewerSelector)::DownCast (aSelectorsIter.Key()); + if (aSelector->Contains (theObject)) + { + aSelector->myTolerances.Decrement (aPrevSens); + aSelector->myTolerances.Add (theNewSens); + aSelector->myToUpdateTolerance = Standard_True; + } + } + } + if (myLocal.IsBound (theObject)) + { + const SelectMgr_SequenceOfSelector& aSelectors = myLocal (theObject); + for (Standard_Integer aSelectorsIdx = 1; aSelectorsIdx <= aSelectors.Length(); aSelectorsIdx++) + { + Handle(SelectMgr_ViewerSelector) aCurSel = Handle(SelectMgr_ViewerSelector)::DownCast (aSelectors (aSelectorsIdx)); + aCurSel->myTolerances.Decrement (aPrevSens); + aCurSel->myTolerances.Add (theNewSens); + aCurSel->myToUpdateTolerance = Standard_True; + } + } +} diff --git a/src/ViewerTest/ViewerTest.cxx b/src/ViewerTest/ViewerTest.cxx index b3b020f6fb..6d70318963 100644 --- a/src/ViewerTest/ViewerTest.cxx +++ b/src/ViewerTest/ViewerTest.cxx @@ -1498,6 +1498,10 @@ struct ViewerTest_AspectsChangeSet Standard_Integer ToSetFreeBoundaryColor; Quantity_Color FreeBoundaryColor; + Standard_Integer ToSetSensitivity; + Standard_Integer SelectionMode; + Standard_Integer Sensitivity; + //! Empty constructor ViewerTest_AspectsChangeSet() : ToSetVisibility (0), @@ -1514,7 +1518,10 @@ struct ViewerTest_AspectsChangeSet ToSetFreeBoundaryWidth (0), FreeBoundaryWidth (1.0), ToSetFreeBoundaryColor (0), - FreeBoundaryColor (DEFAULT_FREEBOUNDARY_COLOR) {} + FreeBoundaryColor (DEFAULT_FREEBOUNDARY_COLOR), + ToSetSensitivity (0), + SelectionMode (-1), + Sensitivity (-1) {} //! @return true if no changes have been requested Standard_Boolean IsEmpty() const @@ -1526,7 +1533,8 @@ struct ViewerTest_AspectsChangeSet && ToSetMaterial == 0 && ToSetShowFreeBoundary == 0 && ToSetFreeBoundaryColor == 0 - && ToSetFreeBoundaryWidth == 0; + && ToSetFreeBoundaryWidth == 0 + && ToSetSensitivity == 0; } //! @return true if properties are valid @@ -1568,6 +1576,11 @@ struct ViewerTest_AspectsChangeSet std::cout << "Error: the free boundary width should be within [1; 10] range (specified " << FreeBoundaryWidth << ")\n"; isOk = Standard_False; } + if (Sensitivity <= 0 && ToSetSensitivity) + { + std::cout << "Error: sensitivity parameter value should be positive (specified " << Sensitivity << ")\n"; + isOk = Standard_False; + } return isOk; } @@ -2056,6 +2069,29 @@ static Standard_Integer VAspects (Draw_Interpretor& /*theDI*/, aChangeSet->ToSetFreeBoundaryWidth = -1; aChangeSet->FreeBoundaryWidth = 1.0; } + else if (anArg == "-setsensitivity") + { + if (isDefaults) + { + std::cout << "Error: wrong syntax. -setSensitivity can not be used together with -defaults call!\n"; + return 1; + } + + if (aNames.IsEmpty()) + { + std::cout << "Error: object and selection mode should specified explicitly when -setSensitivity is used!\n"; + return 1; + } + + if (anArgIter + 2 >= theArgNb) + { + std::cout << "Error: wrong syntax at " << anArg << "\n"; + return 1; + } + aChangeSet->ToSetSensitivity = 1; + aChangeSet->SelectionMode = Draw::Atoi (theArgVec[++anArgIter]); + aChangeSet->Sensitivity = Draw::Atoi (theArgVec[++anArgIter]); + } else { std::cout << "Error: wrong syntax at " << anArg << "\n"; @@ -2208,6 +2244,10 @@ static Standard_Integer VAspects (Draw_Interpretor& /*theDI*/, { aCtx->UnsetWidth (aPrs, Standard_False); } + else if (aChangeSet->ToSetSensitivity != 0) + { + aCtx->SetSelectionSensitivity (aPrs, aChangeSet->SelectionMode, aChangeSet->Sensitivity); + } if (!aDrawer.IsNull()) { if (aChangeSet->ToSetShowFreeBoundary == 1) @@ -2265,6 +2305,10 @@ static Standard_Integer VAspects (Draw_Interpretor& /*theDI*/, { aColoredPrs->UnsetCustomAspects (aSubShape, Standard_True); } + if (aChangeSet->ToSetSensitivity != 0) + { + aCtx->SetSelectionSensitivity (aPrs, aChangeSet->SelectionMode, aChangeSet->Sensitivity); + } } } if (toDisplay) @@ -5321,6 +5365,7 @@ void ViewerTest::Commands(Draw_Interpretor& theCommands) "\n\t\t: [-setFreeBoundaryWidth Width] [-unsetFreeBoundaryWidth]" "\n\t\t: [-setFreeBoundaryColor {ColorName | R G B}] [-unsetFreeBoundaryColor]" "\n\t\t: [-subshapes subname1 [subname2 [...]]]" + "\n\t\t: [-setSensitivity {selection_mode} {value}]" "\n\t\t: Manage presentation properties of all, selected or named objects." "\n\t\t: When -subshapes is specified than following properties will be" "\n\t\t: assigned to specified sub-shapes." diff --git a/tests/bugs/vis/bug26462_1 b/tests/bugs/vis/bug26462_1 new file mode 100644 index 0000000000..68ffc0aacb --- /dev/null +++ b/tests/bugs/vis/bug26462_1 @@ -0,0 +1,53 @@ +puts "============" +puts "CR26462" +puts "============" +puts "" + +########################################################################################## +puts "Visualization - selection does not adapt to line width change" +########################################################################################## + +pload VISUALIZATION MODELING + +vinit +box b1 10 10 10 +box b2 10 10 10 + +vdisplay b1 b2 +vsetlocation b1 10 10 0 +vfit + +vaspects b1 b2 -setWidth 3 + +vselmode 2 1 +# try to select b1 and b2 +vselect 305 322 1 +vselect 103 322 1 +# check that both boxes were not selected with default tolerance value +set aNbSelected [vnbselected] +if {$aNbSelected != "0"} { + puts "ERROR: no boxes must be selected!" +} + +# increase tolerance for b1 +vaspects b1 -setSensitivity 2 4 + +# enable selection with local context opened +vselmode 2 1 +# select edge of b1 +vselect 305 322 +# check that b1's edge was selected +set aNbSelected [vnbselected] +if {$aNbSelected != "1"} { + puts "ERROR: b1 was not selected" +} +# try to select b2 +vselect 103 322 1 +# check that increase of tolerance for b1 doesn't influence +# on b2 +set aNbSelected [vnbselected] +if {$aNbSelected != "1"} { + puts "ERROR: b2 is selected after b1's tolerance increased" +} + +set only_screen 1 diff --git a/tests/bugs/vis/bug26462_2 b/tests/bugs/vis/bug26462_2 new file mode 100644 index 0000000000..78407185cc --- /dev/null +++ b/tests/bugs/vis/bug26462_2 @@ -0,0 +1,15 @@ +puts "============" +puts "CR26462" +puts "============" +puts "" + +########################################################################################## +puts "Visualization - selection does not adapt to line width change" +########################################################################################## + +pload VISUALIZATION QAcommands + +vinit +OCC26462 + +set only_screen 1 -- 2.39.5