0024002: Overall code and build procedure refactoring -- automatic
[occt.git] / src / AIS / AIS_InteractiveObject.cxx
1 // Created on: 1996-12-18
2 // Created by: Robert COUBLANC
3 // Copyright (c) 1996-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16
17 // Modified :   22/03/04 ; SAN : OCC4895 High-level interface for controlling polygon offsets 
18
19 #include <AIS_GraphicTool.hxx>
20 #include <AIS_InteractiveContext.hxx>
21 #include <AIS_InteractiveObject.hxx>
22 #include <Aspect_PolygonOffsetMode.hxx>
23 #include <Bnd_Box.hxx>
24 #include <Graphic3d_AspectFillArea3d.hxx>
25 #include <Graphic3d_AspectLine3d.hxx>
26 #include <Graphic3d_AspectMarker3d.hxx>
27 #include <Graphic3d_AspectText3d.hxx>
28 #include <Graphic3d_BndBox4f.hxx>
29 #include <Graphic3d_CStructure.hxx>
30 #include <Graphic3d_Group.hxx>
31 #include <Graphic3d_MaterialAspect.hxx>
32 #include <Graphic3d_Structure.hxx>
33 #include <Prs3d_BasicAspect.hxx>
34 #include <Prs3d_LineAspect.hxx>
35 #include <Prs3d_PointAspect.hxx>
36 #include <Prs3d_Presentation.hxx>
37 #include <Prs3d_Root.hxx>
38 #include <Prs3d_ShadingAspect.hxx>
39 #include <Prs3d_TextAspect.hxx>
40 #include <PrsMgr_ModedPresentation.hxx>
41 #include <PrsMgr_PresentationManager3d.hxx>
42 #include <Quantity_Color.hxx>
43 #include <Standard_Transient.hxx>
44 #include <Standard_Type.hxx>
45 #include <TColStd_ListIteratorOfListOfInteger.hxx>
46
47 //=======================================================================
48 //function : AIS_InteractiveObject
49 //purpose  : 
50 //=======================================================================
51 AIS_InteractiveObject::
52 AIS_InteractiveObject(const PrsMgr_TypeOfPresentation3d aTypeOfPresentation3d):
53 SelectMgr_SelectableObject(aTypeOfPresentation3d),
54 myTransparency(0.),
55 myOwnColor(Quantity_NOC_WHITE),
56 myOwnMaterial(Graphic3d_NOM_DEFAULT),
57 myHilightMode(-1),
58 myOwnWidth(0.0),
59 myInfiniteState(Standard_False),
60 hasOwnColor(Standard_False),
61 hasOwnMaterial(Standard_False),
62 myCurrentFacingModel(Aspect_TOFM_BOTH_SIDE),
63 myRecomputeEveryPrs(Standard_True),
64 myCTXPtr(NULL),
65 mySelPriority(-1),
66 myDisplayMode (-1),
67 mySelectionMode(0),
68 mystate(0)
69 {
70   Handle (AIS_InteractiveContext) Bid;
71   myCTXPtr = Bid.operator->();
72   SetCurrentFacingModel();
73 }
74
75 //=======================================================================
76 //function : Redisplay
77 //purpose  :
78 //=======================================================================
79 void AIS_InteractiveObject::Redisplay (const Standard_Boolean AllModes)
80 {
81   if (myCTXPtr == NULL)
82     return;
83
84   myCTXPtr->Redisplay (this, Standard_False, AllModes);
85 }
86
87 //=======================================================================
88 //function : Type
89 //purpose  : 
90 //=======================================================================
91
92 AIS_KindOfInteractive AIS_InteractiveObject::Type() const 
93 {return AIS_KOI_None;}
94
95 //=======================================================================
96 //function : Signature
97 //purpose  : 
98 //=======================================================================
99
100 Standard_Integer AIS_InteractiveObject::Signature() const 
101 {return -1;}
102
103 //=======================================================================
104 //function : RecomputeEveryPrs
105 //purpose  : 
106 //=======================================================================
107
108 Standard_Boolean  AIS_InteractiveObject::RecomputeEveryPrs() const 
109 {return myRecomputeEveryPrs;}
110
111 //=======================================================================
112 //function : 
113 //purpose  : 
114 //=======================================================================
115 Standard_Boolean AIS_InteractiveObject::HasInteractiveContext() const 
116 {
117   Handle (AIS_InteractiveContext) aNull;
118   return  (myCTXPtr != aNull.operator->());
119 }
120
121 //=======================================================================
122 //function : 
123 //purpose  : 
124 //=======================================================================
125 Handle(AIS_InteractiveContext) AIS_InteractiveObject::GetContext() const 
126 {
127   return myCTXPtr;
128 }
129
130 //=======================================================================
131 //function : 
132 //purpose  : 
133 //=======================================================================
134 void AIS_InteractiveObject::SetContext(const Handle(AIS_InteractiveContext)& aCtx)
135 {
136   myCTXPtr = aCtx.operator->();
137   if( aCtx.IsNull())
138     return;
139   myDrawer->Link(aCtx->DefaultDrawer());
140 }
141
142 //=======================================================================
143 //function : 
144 //purpose  : 
145 //=======================================================================
146 Standard_Boolean AIS_InteractiveObject::HasOwner() const 
147 {
148   return (!myOwner.IsNull());
149 }
150
151
152
153 //=======================================================================
154 //function : 
155 //purpose  : 
156 //=======================================================================
157 void AIS_InteractiveObject::ClearOwner()
158 {
159   myOwner.Nullify();
160 }
161
162 //=======================================================================
163 //function : 
164 //purpose  : 
165 //=======================================================================
166 Standard_Boolean AIS_InteractiveObject::HasUsers() const 
167 {
168   return (!myUsers.IsEmpty());
169 }
170
171
172 //=======================================================================
173 //function : 
174 //purpose  : 
175 //=======================================================================
176 void AIS_InteractiveObject::AddUser(const Handle(Standard_Transient)& aUser)
177 {
178   myUsers.Append(aUser);
179 }
180
181 //=======================================================================
182 //function : 
183 //purpose  : 
184 //=======================================================================
185 void AIS_InteractiveObject::ClearUsers()
186 {
187   myUsers.Clear();
188 }
189
190
191 //=======================================================================
192 //function : 
193 //purpose  : 
194 //=======================================================================
195 void AIS_InteractiveObject::SetDisplayMode(const Standard_Integer aMode)
196 {
197   if( AcceptDisplayMode(aMode) )
198     myDisplayMode = aMode;
199 }
200   
201
202 //=======================================================================
203 //function : 
204 //purpose  : 
205 //=======================================================================
206 void AIS_InteractiveObject::SetSelectionMode(const Standard_Integer aMode)
207 {
208   mySelectionMode = aMode;
209 }
210
211
212
213 //=======================================================================
214 //function : 
215 //purpose  : 
216 //=======================================================================
217 void AIS_InteractiveObject::SetCurrentFacingModel(const Aspect_TypeOfFacingModel aModel) {
218   myCurrentFacingModel = aModel;
219 }
220
221 //=======================================================================
222 //function : CurrentFacingModel
223 //purpose  : 
224 //=======================================================================
225
226 Aspect_TypeOfFacingModel AIS_InteractiveObject::CurrentFacingModel() const {
227   return myCurrentFacingModel;
228 }
229
230 //=======================================================================
231 //function : SetColor
232 //purpose  : 
233 //=======================================================================
234
235 void AIS_InteractiveObject::SetColor(const Quantity_NameOfColor aColor)
236 {
237   SetColor(Quantity_Color(aColor));
238 }
239
240 //=======================================================================
241 //function : SetColor
242 //purpose  : 
243 //=======================================================================
244
245 void AIS_InteractiveObject::SetColor(const Quantity_Color &aColor)
246 {
247   myOwnColor = aColor;
248   hasOwnColor = Standard_True;
249 }
250
251 //=======================================================================
252 //function : UnsetColor
253 //purpose  : 
254 //=======================================================================
255 void AIS_InteractiveObject::UnsetColor()
256 {
257   hasOwnColor = Standard_False;
258 }
259
260 //=======================================================================
261 //function : 
262 //purpose  : 
263 //=======================================================================
264 void AIS_InteractiveObject::SetWidth(const Standard_Real aValue)
265 {
266   myOwnWidth = aValue;
267 }
268
269 //=======================================================================
270 //function : 
271 //purpose  : 
272 //=======================================================================
273 void AIS_InteractiveObject::UnsetWidth()
274 {
275   myOwnWidth = 0.;
276 }
277
278
279 //=======================================================================
280 //function : 
281 //purpose  : 
282 //=======================================================================
283 void AIS_InteractiveObject::SetMaterial(const Graphic3d_NameOfMaterial aName)
284 {
285   if( HasColor() || IsTransparent() || HasMaterial() )
286   {
287     myDrawer->ShadingAspect()->SetMaterial(aName);
288   }
289   else
290   {
291     myDrawer->SetShadingAspect(new Prs3d_ShadingAspect());
292     myDrawer->ShadingAspect()->SetMaterial(aName);
293   }
294   myOwnMaterial  = aName;
295   hasOwnMaterial = Standard_True;
296 }
297
298 //=======================================================================
299 //function : SetMaterial
300 //purpose  : 
301 //=======================================================================
302 void AIS_InteractiveObject::SetMaterial (const Graphic3d_MaterialAspect& theMaterial)
303 {
304   if (!HasColor() && !IsTransparent() && !HasMaterial())
305   {
306     myDrawer->SetShadingAspect (new Prs3d_ShadingAspect);
307   }
308
309   myDrawer->ShadingAspect()->SetMaterial (theMaterial);
310
311   hasOwnMaterial = Standard_True;
312 }
313
314 //=======================================================================
315 //function : UnsetMaterial
316 //purpose  :
317 //=======================================================================
318 void AIS_InteractiveObject::UnsetMaterial()
319 {
320   if (!HasMaterial())
321   {
322     return;
323   }
324
325   if (HasColor() || IsTransparent())
326   {
327     if(myDrawer->HasLink())
328     {
329       myDrawer->ShadingAspect()->SetMaterial (AIS_GraphicTool::GetMaterial (myDrawer->Link()));
330     }
331
332     if (HasColor())
333     {
334       SetColor (myOwnColor);
335     }
336
337     if (IsTransparent())
338     {
339       SetTransparency (myTransparency);
340     }
341   }
342   else
343   {
344     Handle(Prs3d_ShadingAspect) anAspect;
345     myDrawer->SetShadingAspect (anAspect);
346   }
347
348   hasOwnMaterial = Standard_False;
349 }
350
351 //=======================================================================
352 //function : SetTransparency
353 //purpose  : 
354 //=======================================================================
355 void AIS_InteractiveObject::SetTransparency(const Standard_Real aValue)
356 {
357   if(!HasColor() && !IsTransparent() && !HasMaterial())
358   {
359     myDrawer->SetShadingAspect(new Prs3d_ShadingAspect());
360     if(myDrawer->HasLink())
361       myDrawer->ShadingAspect()->SetMaterial(AIS_GraphicTool::GetMaterial(myDrawer->Link()));
362   }
363   Graphic3d_MaterialAspect FMat = myDrawer->ShadingAspect()->Aspect()->FrontMaterial();
364   Graphic3d_MaterialAspect BMat = myDrawer->ShadingAspect()->Aspect()->BackMaterial();
365   FMat.SetTransparency(aValue); BMat.SetTransparency(aValue);
366   myDrawer->ShadingAspect()->Aspect()->SetFrontMaterial(FMat);
367   myDrawer->ShadingAspect()->Aspect()->SetBackMaterial(BMat);
368   myTransparency = aValue;
369 }
370
371 //=======================================================================
372 //function : UnsetTransparency
373 //purpose  : 
374 //=======================================================================
375 void AIS_InteractiveObject::UnsetTransparency()
376 {
377   if(HasColor() || HasMaterial() )
378   {
379     Graphic3d_MaterialAspect FMat = myDrawer->ShadingAspect()->Aspect()->FrontMaterial();
380     Graphic3d_MaterialAspect BMat = myDrawer->ShadingAspect()->Aspect()->BackMaterial();
381     FMat.SetTransparency(0.); BMat.SetTransparency(0.);
382     myDrawer->ShadingAspect()->Aspect()->SetFrontMaterial(FMat);
383     myDrawer->ShadingAspect()->Aspect()->SetBackMaterial(BMat);
384   }
385   else{
386     Handle (Prs3d_ShadingAspect) SA;
387     myDrawer->SetShadingAspect(SA);
388   }
389   myTransparency =0.0;
390 }
391 //=======================================================================
392 //function : Transparency
393 //purpose  : 
394 //=======================================================================
395 Standard_Real AIS_InteractiveObject::Transparency() const 
396 {
397   return (myTransparency<=0.05 ? 0 : myTransparency);
398 // Graphic3d_MaterialAspect Mat = myDrawer->ShadingAspect()->Aspect()->FrontMaterial();
399 // return Mat.Transparency();
400 }
401
402 //=======================================================================
403 //function : UnsetAttributes
404 //purpose  : 
405 //=======================================================================
406 void AIS_InteractiveObject::UnsetAttributes()
407 {
408   SelectMgr_SelectableObject::UnsetAttributes();
409
410   hasOwnColor    = Standard_False;
411   hasOwnMaterial = Standard_False;
412   myOwnWidth     = 0.0;
413   myTransparency = 0.0;
414 }
415
416 //=======================================================================
417 //function : 
418 //purpose  : 
419 //=======================================================================
420 void AIS_InteractiveObject::MustRecomputePrs(const Standard_Integer ) const 
421 {}
422
423 //=======================================================================
424 //function : 
425 //purpose  : 
426 //=======================================================================
427 const TColStd_ListOfInteger& AIS_InteractiveObject::ListOfRecomputeModes() const 
428 {return myToRecomputeModes;}
429
430 //=======================================================================
431 //function : 
432 //purpose  : 
433 //=======================================================================
434 void AIS_InteractiveObject::SetRecomputeOk()
435 {myToRecomputeModes.Clear();}
436
437
438 //=======================================================================
439 //function : AcceptDisplayMode
440 //purpose  : 
441 //=======================================================================
442
443 Standard_Boolean  AIS_InteractiveObject::AcceptDisplayMode(const Standard_Integer ) const
444 {return Standard_True;}
445
446 //=======================================================================
447 //function : DefaultDisplayMode
448 //purpose  : 
449 //=======================================================================
450
451 Standard_Integer AIS_InteractiveObject::DefaultDisplayMode() const
452 {return 0;}
453
454
455 //=======================================================================
456 //function : SetInfiniteState
457 //purpose  : 
458 //=======================================================================
459 void AIS_InteractiveObject::SetInfiniteState(const Standard_Boolean aFlag)
460 {
461   myInfiniteState = aFlag;
462   Handle(Prs3d_Presentation) P;
463
464   for(Standard_Integer i =1; i<=myPresentations.Length();i++)
465   {
466     P = myPresentations(i).Presentation()->Presentation();
467     if(!P.IsNull())
468       P->SetInfiniteState(myInfiniteState);
469   }
470 }
471
472 //=======================================================================
473 //function : HasPresentation
474 //purpose  :
475 //=======================================================================
476 Standard_Boolean AIS_InteractiveObject::HasPresentation() const
477 {
478   return !GetContext().IsNull()
479        && GetContext()->MainPrsMgr()->HasPresentation (this, myDisplayMode);
480 }
481
482 //=======================================================================
483 //function : Presentation
484 //purpose  :
485 //=======================================================================
486 Handle(Prs3d_Presentation) AIS_InteractiveObject::Presentation() const
487 {
488   return HasPresentation()
489        ? GetContext()->MainPrsMgr()->Presentation (this, myDisplayMode)->Presentation()
490        : Handle(Prs3d_Presentation)();
491 }
492
493 //=======================================================================
494 //function : SetAspect 
495 //purpose  : 
496 //=======================================================================
497 void AIS_InteractiveObject::SetAspect(const Handle(Prs3d_BasicAspect)& anAspect,
498                                       const Standard_Boolean globalChange) {
499
500   if( HasPresentation() ) {
501     Handle(Prs3d_Presentation) prs = Presentation();
502     { Handle(Prs3d_ShadingAspect) aspect =
503                         Handle(Prs3d_ShadingAspect)::DownCast(anAspect);
504       if( !aspect.IsNull() ) {
505         if( globalChange ) prs->SetPrimitivesAspect(aspect->Aspect());
506         Prs3d_Root::CurrentGroup(prs)->SetGroupPrimitivesAspect(aspect->Aspect());
507         return;
508       }
509     }
510     { Handle(Prs3d_LineAspect) aspect =
511                         Handle(Prs3d_LineAspect)::DownCast(anAspect);
512       if( !aspect.IsNull() ) {
513         if( globalChange ) prs->SetPrimitivesAspect(aspect->Aspect());
514         Prs3d_Root::CurrentGroup(prs)->SetGroupPrimitivesAspect(aspect->Aspect());
515         return;
516       }
517     }
518     { Handle(Prs3d_PointAspect) aspect =
519                         Handle(Prs3d_PointAspect)::DownCast(anAspect);
520       if( !aspect.IsNull() ) {
521         if( globalChange ) prs->SetPrimitivesAspect(aspect->Aspect());
522         Prs3d_Root::CurrentGroup(prs)->SetGroupPrimitivesAspect(aspect->Aspect());
523         return;
524       }
525     }
526     { Handle(Prs3d_TextAspect) aspect =
527                         Handle(Prs3d_TextAspect)::DownCast(anAspect);
528       if( !aspect.IsNull() ) {
529         if( globalChange ) prs->SetPrimitivesAspect(aspect->Aspect());
530         Prs3d_Root::CurrentGroup(prs)->SetGroupPrimitivesAspect(aspect->Aspect());
531         return;
532       }
533     }
534   }
535 }
536
537 //=======================================================================
538 //function : SetPolygonOffsets 
539 //purpose  : 
540 //======================================================================= 
541 void AIS_InteractiveObject::SetPolygonOffsets(const Standard_Integer    aMode,
542                                               const Standard_ShortReal  aFactor,
543                                               const Standard_ShortReal  aUnits) 
544 {
545   if ( !HasPolygonOffsets() )
546     myDrawer->SetShadingAspect(new Prs3d_ShadingAspect());
547
548   myDrawer->ShadingAspect()->Aspect()->SetPolygonOffsets( aMode, aFactor, aUnits );
549
550   // Modify existing presentations 
551   for (Standard_Integer aPrsIter = 1, n = myPresentations.Length(); aPrsIter <= n; ++aPrsIter)
552   {
553     const Handle(PrsMgr_Presentation)& aPrs3d = myPresentations (aPrsIter).Presentation();
554     if ( !aPrs3d.IsNull() ) {
555       const Handle(Graphic3d_Structure)& aStruct = aPrs3d->Presentation();
556       if( !aStruct.IsNull() ) {
557         aStruct->SetPrimitivesAspect( myDrawer->ShadingAspect()->Aspect() );
558         // Workaround for issue 23115: Need to update also groups, because their
559         // face aspect ALWAYS overrides the structure's.
560         const Graphic3d_SequenceOfGroup& aGroups = aStruct->Groups();
561         for (Graphic3d_SequenceOfGroup::Iterator aGroupIter (aGroups); aGroupIter.More(); aGroupIter.Next())
562         {
563           Handle(Graphic3d_Group)& aGrp = aGroupIter.ChangeValue();
564           if (aGrp.IsNull()
565           || !aGrp->IsGroupPrimitivesAspectSet (Graphic3d_ASPECT_FILL_AREA))
566           {
567             continue;
568           }
569
570           Handle(Graphic3d_AspectFillArea3d) aFaceAsp = new Graphic3d_AspectFillArea3d();
571           Handle(Graphic3d_AspectLine3d)     aLineAsp = new Graphic3d_AspectLine3d();
572           Handle(Graphic3d_AspectMarker3d)   aPntAsp  = new Graphic3d_AspectMarker3d();
573           Handle(Graphic3d_AspectText3d)     aTextAsp = new Graphic3d_AspectText3d();
574           // TODO: Add methods for retrieving individual aspects from Graphic3d_Group
575           aGrp->GroupPrimitivesAspect(aLineAsp, aTextAsp, aPntAsp, aFaceAsp);
576           aFaceAsp->SetPolygonOffsets(aMode, aFactor, aUnits);
577           aGrp->SetGroupPrimitivesAspect(aFaceAsp);
578         }
579       }
580     }
581   }
582 }
583
584 //=======================================================================
585 //function : HasPolygonOffsets 
586 //purpose  : 
587 //=======================================================================
588 Standard_Boolean AIS_InteractiveObject::HasPolygonOffsets() const
589 {
590   return !( myDrawer->ShadingAspect().IsNull() || 
591           ( myDrawer->HasLink() &&
592           myDrawer->ShadingAspect() == myDrawer->Link()->ShadingAspect() ) );
593 }
594
595 //=======================================================================
596 //function : PolygonOffsets 
597 //purpose  : 
598 //=======================================================================
599 void AIS_InteractiveObject::PolygonOffsets(Standard_Integer&    aMode,
600                                            Standard_ShortReal&  aFactor,
601                                            Standard_ShortReal&  aUnits) const 
602 {
603   if( HasPolygonOffsets() )
604     myDrawer->ShadingAspect()->Aspect()->PolygonOffsets( aMode, aFactor, aUnits );
605 }
606
607 //=======================================================================
608 //function : BoundingBox
609 //purpose  : Returns bounding box of object correspondingly to its
610 //           current display mode
611 //=======================================================================
612 void AIS_InteractiveObject::BoundingBox (Bnd_Box& theBndBox)
613 {
614   if (myDisplayMode == -1)
615   {
616     if (!myPresentations.IsEmpty())
617     {
618       const Handle(PrsMgr_Presentation)& aPrs3d = myPresentations.First().Presentation();
619       const Handle(Graphic3d_Structure)& aStruct = aPrs3d->Presentation();
620       const Graphic3d_BndBox4f& aBndBox = aStruct->CStructure()->BoundingBox();
621
622       if (!aBndBox.IsValid())
623       {
624         theBndBox.SetVoid();
625         return;
626       }
627
628       theBndBox.Update (static_cast<Standard_Real> (aBndBox.CornerMin().x()),
629                         static_cast<Standard_Real> (aBndBox.CornerMin().y()),
630                         static_cast<Standard_Real> (aBndBox.CornerMin().z()),
631                         static_cast<Standard_Real> (aBndBox.CornerMax().x()),
632                         static_cast<Standard_Real> (aBndBox.CornerMax().y()),
633                         static_cast<Standard_Real> (aBndBox.CornerMax().z()));
634       return;
635     }
636     else
637     {
638       for (PrsMgr_ListOfPresentableObjectsIter aPrsIter (Children()); aPrsIter.More(); aPrsIter.Next())
639       {
640         Handle(AIS_InteractiveObject) aChild (Handle(AIS_InteractiveObject)::DownCast (aPrsIter.Value()));
641         if (aChild.IsNull())
642         {
643           continue;
644         }
645         Bnd_Box aBox;
646         aChild->BoundingBox (aBox);
647         theBndBox.Add (aBox);
648       }
649       return;
650     }
651   }
652   else
653   {
654     for (Standard_Integer aPrsIter = 1; aPrsIter <= myPresentations.Length(); ++aPrsIter)
655     {
656       if (myPresentations (aPrsIter).Mode() == myDisplayMode)
657       {
658         const Handle(PrsMgr_Presentation)& aPrs3d = myPresentations (aPrsIter).Presentation();
659         const Handle(Graphic3d_Structure)& aStruct = aPrs3d->Presentation();
660         const Graphic3d_BndBox4f& aBndBox = aStruct->CStructure()->BoundingBox();
661
662         if (!aBndBox.IsValid())
663         {
664           theBndBox.SetVoid();
665           return;
666         }
667
668         theBndBox.Update (static_cast<Standard_Real> (aBndBox.CornerMin().x()),
669                           static_cast<Standard_Real> (aBndBox.CornerMin().y()),
670                           static_cast<Standard_Real> (aBndBox.CornerMin().z()),
671                           static_cast<Standard_Real> (aBndBox.CornerMax().x()),
672                           static_cast<Standard_Real> (aBndBox.CornerMax().y()),
673                           static_cast<Standard_Real> (aBndBox.CornerMax().z()));
674         return;
675       }
676     }
677   }
678 }