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