d2d30fa324289d02b3d86cbfa7b4b92515d5ace7
[occt.git] / src / AIS / AIS_AngleDimension.cxx
1 // Created on: 1996-12-05
2 // Created by: Arnaud BOUZY/Odile Olivier
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 #include <AIS_AngleDimension.hxx>
18
19 #include <AIS.hxx>
20 #include <BRepBuilderAPI_MakeFace.hxx>
21 #include <BRepAdaptor_Curve.hxx>
22 #include <BRepAdaptor_Surface.hxx>
23 #include <BRepLib_MakeVertex.hxx>
24 #include <BRep_Tool.hxx>
25 #include <ElCLib.hxx>
26 #include <GCPnts_UniformAbscissa.hxx>
27 #include <GC_MakeArcOfCircle.hxx>
28 #include <gce_MakeLin2d.hxx>
29 #include <gce_MakeLin.hxx>
30 #include <gce_MakeCirc.hxx>
31 #include <gce_MakeCone.hxx>
32 #include <gce_MakePln.hxx>
33 #include <gce_MakeDir.hxx>
34 #include <Geom_Circle.hxx>
35 #include <Geom_TrimmedCurve.hxx>
36 #include <Geom_ConicalSurface.hxx>
37 #include <Geom_SurfaceOfRevolution.hxx>
38 #include <Geom_OffsetSurface.hxx>
39 #include <Graphic3d_ArrayOfSegments.hxx>
40 #include <Graphic3d_Group.hxx>
41 #include <Graphic3d_ArrayOfPolylines.hxx>
42 #include <IntAna2d_AnaIntersection.hxx>
43 #include <ProjLib.hxx>
44 #include <Prs3d_Root.hxx>
45 #include <Prs3d_ShadingAspect.hxx>
46 #include <PrsMgr_PresentationManager3d.hxx>
47 #include <Select3D_SensitiveGroup.hxx>
48 #include <Select3D_SensitiveSegment.hxx>
49 #include <SelectMgr_Selection.hxx>
50 #include <Standard_ProgramError.hxx>
51 #include <UnitsAPI.hxx>
52 #include <Geom_Line.hxx>
53 #include <Geom_Plane.hxx>
54
55
56 IMPLEMENT_STANDARD_RTTIEXT(AIS_AngleDimension,AIS_Dimension)
57
58 namespace
59 {
60   static const TCollection_ExtendedString THE_EMPTY_LABEL_STRING;
61   static const Standard_Real              THE_EMPTY_LABEL_WIDTH = 0.0;
62   static const Standard_ExtCharacter      THE_DEGREE_SYMBOL (0x00B0);
63   static const Standard_Real              THE_3D_TEXT_MARGIN = 0.1;
64 }
65
66 //=======================================================================
67 //function : Constructor
68 //purpose  : 
69 //=======================================================================
70 AIS_AngleDimension::AIS_AngleDimension (const TopoDS_Edge& theFirstEdge,
71                                         const TopoDS_Edge& theSecondEdge)
72 : AIS_Dimension (AIS_KOD_PLANEANGLE)
73 {
74   Init();
75   SetMeasuredGeometry (theFirstEdge, theSecondEdge);
76 }
77
78 //=======================================================================
79 //function : Constructor
80 //purpose  : 
81 //=======================================================================
82 AIS_AngleDimension::AIS_AngleDimension (const gp_Pnt& theFirstPoint,
83                                         const gp_Pnt& theSecondPoint,
84                                         const gp_Pnt& theThirdPoint)
85 : AIS_Dimension (AIS_KOD_PLANEANGLE)
86 {
87   Init();
88   SetMeasuredGeometry (theFirstPoint, theSecondPoint, theThirdPoint);
89 }
90
91 //=======================================================================
92 //function : Constructor
93 //purpose  : 
94 //=======================================================================
95 AIS_AngleDimension::AIS_AngleDimension (const TopoDS_Vertex& theFirstVertex,
96                                         const TopoDS_Vertex& theSecondVertex,
97                                         const TopoDS_Vertex& theThirdVertex)
98 : AIS_Dimension (AIS_KOD_PLANEANGLE)
99 {
100   Init();
101   SetMeasuredGeometry (theFirstVertex, theSecondVertex, theThirdVertex);
102 }
103
104 //=======================================================================
105 //function : Constructor
106 //purpose  : 
107 //=======================================================================
108 AIS_AngleDimension::AIS_AngleDimension (const TopoDS_Face& theCone)
109 : AIS_Dimension (AIS_KOD_PLANEANGLE)
110 {
111   Init();
112   SetMeasuredGeometry (theCone);
113 }
114
115 //=======================================================================
116 //function : Constructor
117 //purpose  : 
118 //=======================================================================
119 AIS_AngleDimension::AIS_AngleDimension (const TopoDS_Face& theFirstFace,
120                                         const TopoDS_Face& theSecondFace)
121 : AIS_Dimension (AIS_KOD_PLANEANGLE)
122 {
123   Init();
124   SetMeasuredGeometry (theFirstFace, theSecondFace);
125 }
126
127 //=======================================================================
128 //function : Constructor
129 //purpose  : 
130 //=======================================================================
131 AIS_AngleDimension::AIS_AngleDimension (const TopoDS_Face& theFirstFace,
132                                         const TopoDS_Face& theSecondFace,
133                                         const gp_Pnt& thePoint)
134 : AIS_Dimension (AIS_KOD_PLANEANGLE)
135 {
136   Init();
137   SetMeasuredGeometry (theFirstFace, theSecondFace, thePoint);
138 }
139
140 //=======================================================================
141 //function : SetMeasuredGeometry
142 //purpose  : 
143 //=======================================================================
144 void AIS_AngleDimension::SetMeasuredGeometry (const TopoDS_Edge& theFirstEdge,
145                                               const TopoDS_Edge& theSecondEdge)
146 {
147   gp_Pln aComputedPlane;
148
149   myFirstShape      = theFirstEdge;
150   mySecondShape     = theSecondEdge;
151   myThirdShape      = TopoDS_Shape();
152   myGeometryType    = GeometryType_Edges;
153   myIsGeometryValid = InitTwoEdgesAngle (aComputedPlane);
154
155   if (myIsGeometryValid && !myIsPlaneCustom)
156   {
157     myPlane = aComputedPlane;
158   }
159
160   SetToUpdate();
161 }
162
163 //=======================================================================
164 //function : SetMeasuredGeometry
165 //purpose  : 
166 //=======================================================================
167 void AIS_AngleDimension::SetMeasuredGeometry (const gp_Pnt& theFirstPoint,
168                                               const gp_Pnt& theSecondPoint,
169                                               const gp_Pnt& theThirdPoint)
170 {
171   myFirstPoint    = theFirstPoint;
172   myCenterPoint   = theSecondPoint;
173   mySecondPoint   = theThirdPoint;
174   myFirstShape    = BRepLib_MakeVertex (myFirstPoint);
175   mySecondShape   = BRepLib_MakeVertex (myCenterPoint);
176   myThirdShape    = BRepLib_MakeVertex (mySecondPoint);
177   myGeometryType  = GeometryType_Points;
178   myIsGeometryValid       = IsValidPoints (myFirstPoint, myCenterPoint, mySecondPoint);
179
180   if (myIsGeometryValid && !myIsPlaneCustom)
181   {
182     ComputePlane();
183   }
184
185   SetToUpdate();
186 }
187
188 //=======================================================================
189 //function : SetMeasuredGeometry
190 //purpose  : 
191 //=======================================================================
192 void AIS_AngleDimension::SetMeasuredGeometry (const TopoDS_Vertex& theFirstVertex,
193                                               const TopoDS_Vertex& theSecondVertex,
194                                               const TopoDS_Vertex& theThirdVertex)
195 {
196   myFirstShape      = theFirstVertex;
197   mySecondShape     = theSecondVertex;
198   myThirdShape      = theThirdVertex;
199   myFirstPoint      = BRep_Tool::Pnt (theFirstVertex);
200   myCenterPoint     = BRep_Tool::Pnt (theSecondVertex);
201   mySecondPoint     = BRep_Tool::Pnt (theThirdVertex);
202   myGeometryType    = GeometryType_Points;
203   myIsGeometryValid = IsValidPoints (myFirstPoint, myCenterPoint, mySecondPoint);
204
205   if (myIsGeometryValid && !myIsPlaneCustom)
206   {
207     ComputePlane();
208   }
209
210   SetToUpdate();
211 }
212
213 //=======================================================================
214 //function : SetMeasuredGeometry
215 //purpose  : 
216 //=======================================================================
217 void AIS_AngleDimension::SetMeasuredGeometry (const TopoDS_Face& theCone)
218 {
219   myFirstShape      = theCone;
220   mySecondShape     = TopoDS_Shape();
221   myThirdShape      = TopoDS_Shape();
222   myGeometryType    = GeometryType_Face;
223   myIsGeometryValid = InitConeAngle();
224
225   if (myIsGeometryValid && !myIsPlaneCustom)
226   {
227     ComputePlane();
228   }
229
230   SetToUpdate();
231 }
232
233 //=======================================================================
234 //function : SetMeasuredGeometry
235 //purpose  : 
236 //=======================================================================
237 void AIS_AngleDimension::SetMeasuredGeometry (const TopoDS_Face& theFirstFace,
238                                               const TopoDS_Face& theSecondFace)
239 {
240   myFirstShape      = theFirstFace;
241   mySecondShape     = theSecondFace;
242   myThirdShape      = TopoDS_Shape();
243   myGeometryType    = GeometryType_Faces;
244   myIsGeometryValid = InitTwoFacesAngle();
245
246   if (myIsGeometryValid && !myIsPlaneCustom)
247   {
248     ComputePlane();
249   }
250
251   SetToUpdate();
252 }
253
254 //=======================================================================
255 //function : SetMeasuredGeometry
256 //purpose  : 
257 //=======================================================================
258 void AIS_AngleDimension::SetMeasuredGeometry (const TopoDS_Face& theFirstFace,
259                                               const TopoDS_Face& theSecondFace,
260                                               const gp_Pnt& thePoint)
261 {
262   myFirstShape      = theFirstFace;
263   mySecondShape     = theSecondFace;
264   myThirdShape      = TopoDS_Shape();
265   myGeometryType    = GeometryType_Faces;
266   myIsGeometryValid = InitTwoFacesAngle (thePoint);
267
268   if (myIsGeometryValid && !myIsPlaneCustom)
269   {
270     ComputePlane();
271   }
272
273   SetToUpdate();
274 }
275
276 //=======================================================================
277 //function : Init
278 //purpose  : 
279 //=======================================================================
280 void AIS_AngleDimension::Init()
281 {
282   SetSpecialSymbol (THE_DEGREE_SYMBOL);
283   SetDisplaySpecialSymbol (AIS_DSS_After);
284   SetFlyout (15.0);
285 }
286
287 //=======================================================================
288 //function: GetCenterOnArc
289 //purpose :
290 //=======================================================================
291 gp_Pnt AIS_AngleDimension::GetCenterOnArc (const gp_Pnt& theFirstAttach,
292                                            const gp_Pnt& theSecondAttach,
293                                            const gp_Pnt& theCenter) const
294 {
295   // construct plane where the circle and the arc are located
296   gce_MakePln aConstructPlane (theFirstAttach, theSecondAttach, theCenter);
297   if (!aConstructPlane.IsDone())
298   {
299     return gp::Origin();
300   }
301   
302   gp_Pln aPlane = aConstructPlane.Value();
303
304   Standard_Real aRadius = theFirstAttach.Distance (theCenter);
305
306   // construct circle forming the arc
307   gce_MakeCirc aConstructCircle (theCenter, aPlane, aRadius);
308   if (!aConstructCircle.IsDone())
309   {
310     return gp::Origin();
311   }
312
313   gp_Circ aCircle = aConstructCircle.Value();
314
315   // compute angle parameters of arc end-points on circle
316   Standard_Real aParamBeg = ElCLib::Parameter (aCircle, theFirstAttach);
317   Standard_Real aParamEnd = ElCLib::Parameter (aCircle, theSecondAttach);
318   ElCLib::AdjustPeriodic (0.0, M_PI * 2, Precision::PConfusion(), aParamBeg, aParamEnd);
319
320   return ElCLib::Value ((aParamBeg + aParamEnd) * 0.5, aCircle);
321 }
322
323 //=======================================================================
324 //function : GetNormalForMinAngle
325 //purpose  :
326 //=======================================================================
327 gp_Dir AIS_AngleDimension::GetNormalForMinAngle() const
328 {
329   const gp_Dir& aNormal = myPlane.Axis().Direction();
330   gp_Dir aFirst (gp_Vec (myCenterPoint, myFirstPoint) );
331   gp_Dir aSecond (gp_Vec (myCenterPoint, mySecondPoint) );
332
333   return aFirst.AngleWithRef (aSecond, aNormal) < 0.0
334     ? aNormal.Reversed()
335     : aNormal;
336 }
337
338 //=======================================================================
339 //function : DrawArc
340 //purpose  : draws the arc between two attach points
341 //=======================================================================
342 void AIS_AngleDimension::DrawArc (const Handle(Prs3d_Presentation)& thePresentation,
343                                   const gp_Pnt& theFirstAttach,
344                                   const gp_Pnt& theSecondAttach,
345                                   const gp_Pnt& theCenter,
346                                   const Standard_Real theRadius,
347                                   const Standard_Integer theMode)
348 {
349   gp_Pln aPlane (myCenterPoint, GetNormalForMinAngle());
350
351   // construct circle forming the arc
352   gce_MakeCirc aConstructCircle (theCenter, aPlane, theRadius);
353   if (!aConstructCircle.IsDone())
354   {
355     return;
356   }
357
358   gp_Circ aCircle = aConstructCircle.Value();
359
360   // construct the arc
361   GC_MakeArcOfCircle aConstructArc (aCircle, theFirstAttach, theSecondAttach, Standard_True);
362   if (!aConstructArc.IsDone())
363   {
364     return;
365   }
366
367   // generate points with specified deflection
368   const Handle(Geom_TrimmedCurve)& anArcCurve = aConstructArc.Value();
369   
370   GeomAdaptor_Curve anArcAdaptor (anArcCurve, anArcCurve->FirstParameter(), anArcCurve->LastParameter());
371
372   // compute number of discretization elements in old-fanshioned way
373   gp_Vec aCenterToFirstVec  (theCenter, theFirstAttach);
374   gp_Vec aCenterToSecondVec (theCenter, theSecondAttach);
375   const Standard_Real anAngle = aCenterToFirstVec.Angle (aCenterToSecondVec);
376   const Standard_Integer aNbPoints = Max (4, Standard_Integer (50.0 * anAngle / M_PI));
377
378   GCPnts_UniformAbscissa aMakePnts (anArcAdaptor, aNbPoints);
379   if (!aMakePnts.IsDone())
380   {
381     return;
382   }
383
384   // init data arrays for graphical and selection primitives
385   Handle(Graphic3d_ArrayOfPolylines) aPrimSegments = new Graphic3d_ArrayOfPolylines (aNbPoints);
386
387   SelectionGeometry::Curve& aSensitiveCurve = mySelectionGeom.NewCurve();
388
389   // load data into arrays
390   for (Standard_Integer aPntIt = 1; aPntIt <= aMakePnts.NbPoints(); ++aPntIt)
391   {
392     gp_Pnt aPnt = anArcAdaptor.Value (aMakePnts.Parameter (aPntIt));
393
394     aPrimSegments->AddVertex (aPnt);
395
396     aSensitiveCurve.Append (aPnt);
397   }
398
399   // add display presentation
400   if (!myDrawer->DimensionAspect()->IsText3d() && theMode == ComputeMode_All)
401   {
402     Prs3d_Root::CurrentGroup (thePresentation)->SetStencilTestOptions (Standard_True);
403   }
404   Handle(Graphic3d_AspectLine3d) aDimensionLineStyle = myDrawer->DimensionAspect()->LineAspect()->Aspect();
405   Prs3d_Root::CurrentGroup (thePresentation)->SetPrimitivesAspect (aDimensionLineStyle);
406   Prs3d_Root::CurrentGroup (thePresentation)->AddPrimitiveArray (aPrimSegments);
407   if (!myDrawer->DimensionAspect()->IsText3d() && theMode == ComputeMode_All)
408   {
409     Prs3d_Root::CurrentGroup (thePresentation)->SetStencilTestOptions (Standard_False);
410   }
411 }
412
413 //=======================================================================
414 //function: DrawArcWithText
415 //purpose :
416 //=======================================================================
417 void AIS_AngleDimension::DrawArcWithText (const Handle(Prs3d_Presentation)& thePresentation,
418                                           const gp_Pnt& theFirstAttach,
419                                           const gp_Pnt& theSecondAttach,
420                                           const gp_Pnt& theCenter,
421                                           const TCollection_ExtendedString& theText,
422                                           const Standard_Real theTextWidth,
423                                           const Standard_Integer theMode,
424                                           const Standard_Integer theLabelPosition)
425 {
426   gp_Pln aPlane (myCenterPoint, GetNormalForMinAngle());
427   
428   Standard_Real aRadius = theFirstAttach.Distance (myCenterPoint);
429
430   // construct circle forming the arc
431   gce_MakeCirc aConstructCircle (theCenter, aPlane, aRadius);
432   if (!aConstructCircle.IsDone())
433   {
434     return;
435   }
436
437   gp_Circ aCircle = aConstructCircle.Value();
438
439   // compute angle parameters of arc end-points on circle
440   Standard_Real aParamBeg = ElCLib::Parameter (aCircle, theFirstAttach);
441   Standard_Real aParamEnd = ElCLib::Parameter (aCircle, theSecondAttach);
442   ElCLib::AdjustPeriodic (0.0, M_PI * 2, Precision::PConfusion(), aParamBeg, aParamEnd);
443
444   // middle point of arc parameter on circle
445   Standard_Real aParamMid = (aParamBeg + aParamEnd) * 0.5;
446
447   // add text graphical primitives
448   if (theMode == ComputeMode_All || theMode == ComputeMode_Text)
449   {
450     gp_Pnt aTextPos = ElCLib::Value (aParamMid, aCircle);
451     gp_Dir aTextDir = gce_MakeDir (theFirstAttach, theSecondAttach);
452
453     // Drawing text
454     DrawText (thePresentation,
455               aTextPos,
456               aTextDir,
457               theText,
458               theLabelPosition);
459   }
460
461   if (theMode != ComputeMode_All && theMode != ComputeMode_Line)
462   {
463     return;
464   }
465
466   Handle(Prs3d_DimensionAspect) aDimensionAspect = myDrawer->DimensionAspect();
467
468   Standard_Boolean isLineBreak = aDimensionAspect->TextVerticalPosition() == Prs3d_DTVP_Center
469                               && aDimensionAspect->IsText3d();
470
471   if (isLineBreak)
472   {
473     // compute gap for label as parameteric size of sector on circle segment
474     Standard_Real aSectorOfText = theTextWidth / aRadius;
475     Standard_Real aTextBegin = aParamMid - aSectorOfText * 0.5;
476     Standard_Real aTextEnd = aParamMid + aSectorOfText * 0.5;
477     gp_Pnt aTextPntBeg = ElCLib::Value (aTextBegin, aCircle);
478     gp_Pnt aTextPntEnd = ElCLib::Value (aTextEnd, aCircle);
479
480     // Drawing arcs
481     if (aTextBegin > aParamBeg)
482     {
483       DrawArc (thePresentation, theFirstAttach, aTextPntBeg, theCenter, aRadius, theMode);
484     }
485     if (aTextEnd < aParamEnd)
486     {
487       DrawArc (thePresentation, aTextPntEnd, theSecondAttach, theCenter, aRadius, theMode);
488     }
489   }
490   else
491   {
492     DrawArc (thePresentation, theFirstAttach, theSecondAttach, theCenter, aRadius, theMode);
493   }
494 }
495
496 //=======================================================================
497 //function : CheckPlane
498 //purpose  : 
499 //=======================================================================
500 Standard_Boolean AIS_AngleDimension::CheckPlane (const gp_Pln& thePlane)const
501 {
502   if (!thePlane.Contains (myFirstPoint, Precision::Confusion()) &&
503       !thePlane.Contains (mySecondPoint, Precision::Confusion()) &&
504       !thePlane.Contains (myCenterPoint, Precision::Confusion()))
505   {
506     return Standard_False;
507   }
508
509   return Standard_True;
510 }
511
512 //=======================================================================
513 //function : ComputePlane
514 //purpose  : 
515 //=======================================================================
516 void AIS_AngleDimension::ComputePlane()
517 {
518   if (!myIsGeometryValid)
519   {
520     return;
521   }
522
523   // Compute working plane so that Y axis is codirectional
524   // with Y axis of text coordinate system (necessary for text alignment)
525   gp_Vec aFirstVec   = gp_Vec (myCenterPoint, myFirstPoint);
526   gp_Vec aSecondVec  = gp_Vec (myCenterPoint, mySecondPoint);
527   gp_Vec aDirectionN = aSecondVec ^ aFirstVec;
528   gp_Vec aDirectionY = aFirstVec + aSecondVec;
529   gp_Vec aDirectionX = aDirectionY ^ aDirectionN;
530
531   myPlane = gp_Pln (gp_Ax3 (myCenterPoint, gp_Dir (aDirectionN), gp_Dir (aDirectionX)));
532 }
533
534 //=======================================================================
535 //function : GetModelUnits
536 //purpose  :
537 //=======================================================================
538 const TCollection_AsciiString& AIS_AngleDimension::GetModelUnits() const
539 {
540   return myDrawer->DimAngleModelUnits();
541 }
542
543 //=======================================================================
544 //function : GetDisplayUnits
545 //purpose  :
546 //=======================================================================
547 const TCollection_AsciiString& AIS_AngleDimension::GetDisplayUnits() const
548 {
549   return myDrawer->DimAngleDisplayUnits();
550 }
551
552 //=======================================================================
553 //function : SetModelUnits
554 //purpose  :
555 //=======================================================================
556 void AIS_AngleDimension::SetModelUnits (const TCollection_AsciiString& theUnits)
557 {
558   myDrawer->SetDimAngleModelUnits (theUnits);
559 }
560
561 //=======================================================================
562 //function : SetDisplayUnits
563 //purpose  :
564 //=======================================================================
565 void AIS_AngleDimension::SetDisplayUnits (const TCollection_AsciiString& theUnits)
566 {
567   myDrawer->SetDimAngleDisplayUnits (theUnits);
568 }
569
570 //=======================================================================
571 //function : ComputeValue
572 //purpose  : 
573 //=======================================================================
574 Standard_Real AIS_AngleDimension::ComputeValue() const
575 {
576   if (!IsValid())
577   {
578     return 0.0;
579   }
580
581   gp_Vec aVec1 (myCenterPoint, myFirstPoint);
582   gp_Vec aVec2 (myCenterPoint, mySecondPoint);
583
584   Standard_Real anAngle = aVec1.AngleWithRef (aVec2, GetNormalForMinAngle());
585
586   return anAngle > 0.0 ? anAngle : (2.0 * M_PI + anAngle);
587 }
588
589 //=======================================================================
590 //function : Compute
591 //purpose  : Having three gp_Pnt points compute presentation
592 //=======================================================================
593 void AIS_AngleDimension::Compute (const Handle(PrsMgr_PresentationManager3d)& /*thePM*/,
594                                   const Handle(Prs3d_Presentation)& thePresentation,
595                                   const Standard_Integer theMode)
596 {
597   thePresentation->Clear();
598   mySelectionGeom.Clear (theMode);
599
600   if (!IsValid())
601   {
602     return;
603   }
604
605   // Parameters for presentation
606   Handle(Prs3d_DimensionAspect) aDimensionAspect = myDrawer->DimensionAspect();
607
608   Prs3d_Root::CurrentGroup(thePresentation)->SetPrimitivesAspect (aDimensionAspect->LineAspect()->Aspect());
609
610   Quantity_Length anArrowLength = aDimensionAspect->ArrowAspect()->Length();
611
612   // prepare label string and compute its geometrical width
613   Standard_Real aLabelWidth;
614   TCollection_ExtendedString aLabelString = GetValueString (aLabelWidth);
615
616   // add margins to label width
617   if (aDimensionAspect->IsText3d())
618   {
619     aLabelWidth += aDimensionAspect->TextAspect()->Height() * THE_3D_TEXT_MARGIN * 2.0;
620   }
621
622   // Get parameters from aspect or adjust it according with custom text position
623   Standard_Real anExtensionSize = aDimensionAspect->ExtensionSize();
624   Prs3d_DimensionTextHorizontalPosition aHorisontalTextPos = aDimensionAspect->TextHorizontalPosition();
625
626   if (IsTextPositionCustom())
627   {
628     AdjustParameters (myFixedTextPosition,anExtensionSize, aHorisontalTextPos, myFlyout);
629   }
630
631   // Handle user-defined and automatic arrow placement
632   Standard_Boolean isArrowsExternal = Standard_False;
633   Standard_Integer aLabelPosition = LabelPosition_None;
634
635   FitTextAlignment (aHorisontalTextPos, aLabelPosition, isArrowsExternal);
636
637   gp_Pnt aFirstAttach = myCenterPoint.Translated (gp_Vec(myCenterPoint, myFirstPoint).Normalized() * GetFlyout());
638   gp_Pnt aSecondAttach = myCenterPoint.Translated (gp_Vec(myCenterPoint, mySecondPoint).Normalized() * GetFlyout());
639
640   //Arrows positions and directions
641   gp_Vec aWorkingPlaneDir (GetNormalForMinAngle());
642
643   gp_Dir aFirstExtensionDir  = aWorkingPlaneDir.Reversed() ^ gp_Vec (myCenterPoint, aFirstAttach);
644   gp_Dir aSecondExtensionDir = aWorkingPlaneDir            ^ gp_Vec (myCenterPoint, aSecondAttach);
645
646   gp_Vec aFirstArrowVec  = gp_Vec (aFirstExtensionDir)  * anArrowLength;
647   gp_Vec aSecondArrowVec = gp_Vec (aSecondExtensionDir) * anArrowLength;
648
649   if (isArrowsExternal)
650   {
651     aFirstArrowVec.Reverse();
652     aSecondArrowVec.Reverse();
653   }
654
655   gp_Pnt aFirstArrowBegin  (0.0, 0.0, 0.0);
656   gp_Pnt aFirstArrowEnd    (0.0, 0.0, 0.0);
657   gp_Pnt aSecondArrowBegin (0.0, 0.0, 0.0);
658   gp_Pnt aSecondArrowEnd   (0.0, 0.0, 0.0);
659
660   aFirstArrowBegin  = aFirstAttach;
661   aSecondArrowBegin = aSecondAttach;
662   aFirstArrowEnd    = aFirstAttach.Translated (-aFirstArrowVec);
663   aSecondArrowEnd   = aSecondAttach.Translated (-aSecondArrowVec);
664
665   // Group1: stenciling text and the angle dimension arc
666   Prs3d_Root::NewGroup (thePresentation);
667
668   Standard_Integer aHPosition = aLabelPosition & LabelPosition_HMask;
669
670   // draw text label
671   switch (aHPosition)
672   {
673     case LabelPosition_HCenter :
674     {
675       Standard_Boolean isLineBreak = aDimensionAspect->TextVerticalPosition() == Prs3d_DTVP_Center
676                                   && aDimensionAspect->IsText3d();
677
678       if (isLineBreak)
679       {
680         DrawArcWithText (thePresentation,
681                          aFirstAttach,
682                          aSecondAttach,
683                          myCenterPoint,
684                          aLabelString,
685                          aLabelWidth,
686                          theMode,
687                          aLabelPosition);
688         break;
689       }
690
691       // compute text primitives
692       if (theMode == ComputeMode_All || theMode == ComputeMode_Text)
693       {
694         gp_Vec aDimensionDir (aFirstAttach, aSecondAttach);
695         gp_Pnt aTextPos = IsTextPositionCustom() ? myFixedTextPosition
696                                                 : GetCenterOnArc (aFirstAttach, aSecondAttach, myCenterPoint);
697         gp_Dir aTextDir = aDimensionDir;
698
699         DrawText (thePresentation,
700                   aTextPos,
701                   aTextDir,
702                   aLabelString,
703                   aLabelPosition);
704       }
705
706       if (theMode == ComputeMode_All || theMode == ComputeMode_Line)
707       {
708         DrawArc (thePresentation,
709                  isArrowsExternal ? aFirstAttach : aFirstArrowEnd,
710                  isArrowsExternal ? aSecondAttach : aSecondArrowEnd,
711                  myCenterPoint,
712                  Abs (GetFlyout()),
713                  theMode);
714       }
715     }
716     break;
717
718     case LabelPosition_Left :
719     {
720       DrawExtension (thePresentation,
721                      anExtensionSize,
722                      isArrowsExternal ? aFirstArrowEnd : aFirstAttach,
723                      aFirstExtensionDir,
724                      aLabelString,
725                      aLabelWidth,
726                      theMode,
727                      aLabelPosition);
728     }
729     break;
730
731     case LabelPosition_Right :
732     {
733       DrawExtension (thePresentation,
734                      anExtensionSize,
735                      isArrowsExternal ? aSecondArrowEnd : aSecondAttach,
736                      aSecondExtensionDir,
737                      aLabelString,
738                      aLabelWidth,
739                      theMode,
740                      aLabelPosition);
741     }
742     break;
743   }
744
745   // dimension arc without text
746   if ((theMode == ComputeMode_All || theMode == ComputeMode_Line) && aHPosition != LabelPosition_HCenter)
747   {
748     Prs3d_Root::NewGroup (thePresentation);
749
750     DrawArc (thePresentation,
751              isArrowsExternal ? aFirstAttach  : aFirstArrowEnd,
752              isArrowsExternal ? aSecondAttach : aSecondArrowEnd,
753              myCenterPoint,
754              Abs(GetFlyout ()),
755              theMode);
756   }
757
758   // arrows and arrow extensions
759   if (theMode == ComputeMode_All || theMode == ComputeMode_Line)
760   {
761     Prs3d_Root::NewGroup (thePresentation);
762
763     DrawArrow (thePresentation, aFirstArrowBegin,  gp_Dir (aFirstArrowVec));
764     DrawArrow (thePresentation, aSecondArrowBegin, gp_Dir (aSecondArrowVec));
765   }
766
767   if ((theMode == ComputeMode_All || theMode == ComputeMode_Line) && isArrowsExternal)
768   {
769     Prs3d_Root::NewGroup (thePresentation);
770
771     if (aHPosition != LabelPosition_Left)
772     {
773       DrawExtension (thePresentation,
774                      aDimensionAspect->ArrowTailSize(),
775                      aFirstArrowEnd,
776                      aFirstExtensionDir,
777                      THE_EMPTY_LABEL_STRING,
778                      THE_EMPTY_LABEL_WIDTH,
779                      theMode,
780                      LabelPosition_None);
781     }
782
783     if (aHPosition != LabelPosition_Right)
784     {
785       DrawExtension (thePresentation,
786                      aDimensionAspect->ArrowTailSize(),
787                      aSecondArrowEnd,
788                      aSecondExtensionDir,
789                      THE_EMPTY_LABEL_STRING,
790                      THE_EMPTY_LABEL_WIDTH,
791                      theMode,
792                      LabelPosition_None);
793     }
794   }
795
796   // flyouts
797   if (theMode == ComputeMode_All)
798   {
799     Prs3d_Root::NewGroup (thePresentation);
800
801     Handle(Graphic3d_ArrayOfSegments) aPrimSegments = new Graphic3d_ArrayOfSegments (4);
802     aPrimSegments->AddVertex (myCenterPoint);
803     aPrimSegments->AddVertex (aFirstAttach);
804     aPrimSegments->AddVertex (myCenterPoint);
805     aPrimSegments->AddVertex (aSecondAttach);
806
807     Handle(Graphic3d_AspectLine3d) aFlyoutStyle = myDrawer->DimensionAspect()->LineAspect()->Aspect();
808     Prs3d_Root::CurrentGroup (thePresentation)->SetPrimitivesAspect (aFlyoutStyle);
809     Prs3d_Root::CurrentGroup (thePresentation)->AddPrimitiveArray (aPrimSegments);
810   }
811
812   mySelectionGeom.IsComputed = Standard_True;
813 }
814
815 //=======================================================================
816 //function : ComputeFlyoutSelection
817 //purpose  : computes selection for flyouts
818 //=======================================================================
819 void AIS_AngleDimension::ComputeFlyoutSelection (const Handle(SelectMgr_Selection)& theSelection,
820                                                  const Handle(SelectMgr_EntityOwner)& theOwner)
821 {
822   gp_Pnt aFirstAttach  = myCenterPoint.Translated (gp_Vec (myCenterPoint, myFirstPoint).Normalized()  * GetFlyout());
823   gp_Pnt aSecondAttach = myCenterPoint.Translated (gp_Vec (myCenterPoint, mySecondPoint).Normalized() * GetFlyout());
824
825   Handle(Select3D_SensitiveGroup) aSensitiveEntity = new Select3D_SensitiveGroup (theOwner);
826   aSensitiveEntity->Add (new Select3D_SensitiveSegment (theOwner, myCenterPoint, aFirstAttach));
827   aSensitiveEntity->Add (new Select3D_SensitiveSegment (theOwner, myCenterPoint, aSecondAttach));
828
829   theSelection->Add (aSensitiveEntity);
830 }
831
832 //=======================================================================
833 //function : InitTwoEdgesAngle
834 //purpose  : 
835 //=======================================================================
836 Standard_Boolean AIS_AngleDimension::InitTwoEdgesAngle (gp_Pln& theComputedPlane)
837 {
838   TopoDS_Edge aFirstEdge  = TopoDS::Edge (myFirstShape);
839   TopoDS_Edge aSecondEdge = TopoDS::Edge (mySecondShape);
840
841   BRepAdaptor_Curve aMakeFirstLine  (aFirstEdge);
842   BRepAdaptor_Curve aMakeSecondLine (aSecondEdge);
843
844   if (aMakeFirstLine.GetType() != GeomAbs_Line || aMakeSecondLine.GetType() != GeomAbs_Line)
845   {
846     return  Standard_False;
847   }
848
849   Handle(Geom_Line) aFirstLine  = new Geom_Line (aMakeFirstLine.Line());
850   Handle(Geom_Line) aSecondLine = new Geom_Line (aMakeSecondLine.Line());
851
852   gp_Lin aFirstLin  = aFirstLine->Lin();
853   gp_Lin aSecondLin = aSecondLine->Lin();
854
855   Standard_Boolean isParallelLines = aFirstLin.Direction().IsParallel (aSecondLin.Direction(), Precision::Angular());
856
857   theComputedPlane = isParallelLines ? gp_Pln(gp::XOY())
858                                      : gp_Pln (aSecondLin.Location(), gp_Vec (aFirstLin.Direction()) ^ gp_Vec (aSecondLin.Direction()));
859
860   // Compute geometry for this plane and edges
861   Standard_Boolean isInfinite1,isInfinite2;
862   gp_Pnt aFirstPoint1, aLastPoint1, aFirstPoint2, aLastPoint2;
863   Handle(Geom_Curve) aFirstCurve = aFirstLine, aSecondCurve = aSecondLine;
864   if (!AIS::ComputeGeometry (aFirstEdge, aSecondEdge,
865                              aFirstCurve, aSecondCurve,
866                              aFirstPoint1, aLastPoint1,
867                              aFirstPoint2, aLastPoint2,
868                              isInfinite1, isInfinite2))
869   {
870     return Standard_False;
871   }
872
873   Standard_Boolean isSameLines = aFirstLin.Direction().IsEqual (aSecondLin.Direction(), Precision::Angular())
874                               && aFirstLin.Location().IsEqual (aSecondLin.Location(),Precision::Confusion());
875
876   // It can be the same gp_Lin geometry but the different begin and end parameters
877   Standard_Boolean isSameEdges =
878     (aFirstPoint1.IsEqual (aFirstPoint2, Precision::Confusion()) && aLastPoint1.IsEqual (aLastPoint2, Precision::Confusion()))
879     || (aFirstPoint1.IsEqual (aLastPoint2, Precision::Confusion()) && aLastPoint1.IsEqual (aFirstPoint2, Precision::Confusion()));
880
881   if (isParallelLines)
882   {
883     // Zero angle, it could not handle this geometry
884     if (isSameLines && isSameEdges)
885     {
886       return Standard_False;
887     }
888
889     // Handle the case of Pi angle
890     const Standard_Real aParam11 = ElCLib::Parameter (aFirstLin, aFirstPoint1);
891     const Standard_Real aParam12 = ElCLib::Parameter (aFirstLin, aLastPoint1);
892     const Standard_Real aParam21 = ElCLib::Parameter (aFirstLin, aFirstPoint2);
893     const Standard_Real aParam22 = ElCLib::Parameter (aFirstLin, aLastPoint2);
894     myCenterPoint = ElCLib::Value ( (Min (aParam11, aParam12) + Max (aParam21, aParam22)) * 0.5, aFirstLin);
895     myFirstPoint = myCenterPoint.Translated (gp_Vec (aFirstLin.Direction()) * Abs (GetFlyout()));
896     mySecondPoint = myCenterPoint.XYZ() + (aFirstLin.Direction().IsEqual (aSecondLin.Direction(), Precision::Angular())
897       ? aFirstLin.Direction().Reversed().XYZ() * Abs (GetFlyout())
898       : aSecondLin.Direction().XYZ() * Abs (GetFlyout()));
899   }
900   else
901   {
902     // Find intersection
903     gp_Lin2d aFirstLin2d  = ProjLib::Project (theComputedPlane, aFirstLin);
904     gp_Lin2d aSecondLin2d = ProjLib::Project (theComputedPlane, aSecondLin);
905
906     IntAna2d_AnaIntersection anInt2d (aFirstLin2d, aSecondLin2d);
907     gp_Pnt2d anIntersectPoint;
908     if (!anInt2d.IsDone() || anInt2d.IsEmpty())
909     {
910       return Standard_False;
911     }
912
913     anIntersectPoint = gp_Pnt2d (anInt2d.Point(1).Value());
914     myCenterPoint = ElCLib::To3d (theComputedPlane.Position().Ax2(), anIntersectPoint);
915
916     if (isInfinite1 || isInfinite2)
917     {
918       myFirstPoint  = myCenterPoint.Translated (gp_Vec (aFirstLin.Direction()) * Abs (GetFlyout()));
919       mySecondPoint = myCenterPoint.Translated (gp_Vec (aSecondLin.Direction()) * Abs (GetFlyout()));
920
921       return IsValidPoints (myFirstPoint, myCenterPoint, mySecondPoint);
922     }
923
924     // |
925     // | <- dimension should be here
926     // *----
927     myFirstPoint  = myCenterPoint.Distance (aFirstPoint1) > myCenterPoint.Distance (aLastPoint1)
928                   ? aFirstPoint1
929                   : aLastPoint1;
930
931     mySecondPoint = myCenterPoint.Distance (aFirstPoint2) > myCenterPoint.Distance (aLastPoint2)
932                   ? aFirstPoint2
933                   : aLastPoint2;
934   }
935
936   return IsValidPoints (myFirstPoint, myCenterPoint, mySecondPoint);
937 }
938
939 //=======================================================================
940 //function : InitTwoFacesAngle
941 //purpose  : initialization of angle dimension between two faces
942 //=======================================================================
943 Standard_Boolean AIS_AngleDimension::InitTwoFacesAngle()
944 {
945   TopoDS_Face aFirstFace = TopoDS::Face (myFirstShape);
946   TopoDS_Face aSecondFace = TopoDS::Face (mySecondShape);
947
948   gp_Dir aFirstDir, aSecondDir;
949   gp_Pln aFirstPln, aSecondPln;
950   Handle(Geom_Surface) aFirstBasisSurf, aSecondBasisSurf;
951   AIS_KindOfSurface aFirstSurfType, aSecondSurfType;
952   Standard_Real aFirstOffset, aSecondOffset;
953
954   AIS::GetPlaneFromFace (aFirstFace, aFirstPln,
955                          aFirstBasisSurf,aFirstSurfType,aFirstOffset);
956
957   AIS::GetPlaneFromFace (aSecondFace, aSecondPln,
958                          aSecondBasisSurf, aSecondSurfType, aSecondOffset);
959
960   if (aFirstSurfType == AIS_KOS_Plane && aSecondSurfType == AIS_KOS_Plane)
961   {
962     //Planar faces angle
963     Handle(Geom_Plane) aFirstPlane = Handle(Geom_Plane)::DownCast (aFirstBasisSurf);
964     Handle(Geom_Plane) aSecondPlane = Handle(Geom_Plane)::DownCast (aSecondBasisSurf);
965     return AIS::InitAngleBetweenPlanarFaces (aFirstFace,
966                                              aSecondFace,
967                                              myCenterPoint,
968                                              myFirstPoint,
969                                              mySecondPoint)
970            && IsValidPoints (myFirstPoint,
971                              myCenterPoint,
972                              mySecondPoint);
973   }
974   else
975   {
976     // Curvilinear faces angle
977     return AIS::InitAngleBetweenCurvilinearFaces (aFirstFace,
978                                                   aSecondFace,
979                                                   aFirstSurfType,
980                                                   aSecondSurfType,
981                                                   myCenterPoint,
982                                                   myFirstPoint,
983                                                   mySecondPoint)
984            && IsValidPoints (myFirstPoint,
985                              myCenterPoint,
986                              mySecondPoint);
987   }
988 }
989
990 //=======================================================================
991 //function : InitTwoFacesAngle
992 //purpose  : initialization of angle dimension between two faces
993 //=======================================================================
994 Standard_Boolean AIS_AngleDimension::InitTwoFacesAngle (const gp_Pnt thePointOnFirstFace)
995 {
996   TopoDS_Face aFirstFace = TopoDS::Face (myFirstShape);
997   TopoDS_Face aSecondFace = TopoDS::Face (mySecondShape);
998
999   gp_Dir aFirstDir, aSecondDir;
1000   gp_Pln aFirstPln, aSecondPln;
1001   Handle(Geom_Surface) aFirstBasisSurf, aSecondBasisSurf;
1002   AIS_KindOfSurface aFirstSurfType, aSecondSurfType;
1003   Standard_Real aFirstOffset, aSecondOffset;
1004
1005   AIS::GetPlaneFromFace (aFirstFace, aFirstPln,
1006                          aFirstBasisSurf,aFirstSurfType,aFirstOffset);
1007
1008   AIS::GetPlaneFromFace (aSecondFace, aSecondPln,
1009                          aSecondBasisSurf, aSecondSurfType, aSecondOffset);
1010
1011   myFirstPoint = thePointOnFirstFace;
1012   if (aFirstSurfType == AIS_KOS_Plane && aSecondSurfType == AIS_KOS_Plane)
1013   {
1014     //Planar faces angle
1015     Handle(Geom_Plane) aFirstPlane = Handle(Geom_Plane)::DownCast (aFirstBasisSurf);
1016     Handle(Geom_Plane) aSecondPlane = Handle(Geom_Plane)::DownCast (aSecondBasisSurf);
1017     return AIS::InitAngleBetweenPlanarFaces (aFirstFace,
1018                                              aSecondFace,
1019                                              myCenterPoint,
1020                                              myFirstPoint,
1021                                              mySecondPoint,
1022                                              Standard_True)
1023            && IsValidPoints (myFirstPoint,
1024                              myCenterPoint,
1025                              mySecondPoint);
1026   }
1027   else
1028   {
1029     // Curvilinear faces angle
1030     return AIS::InitAngleBetweenCurvilinearFaces (aFirstFace,
1031                                                   aSecondFace,
1032                                                   aFirstSurfType,
1033                                                   aSecondSurfType,
1034                                                   myCenterPoint,
1035                                                   myFirstPoint,
1036                                                   mySecondPoint,
1037                                                   Standard_True)
1038            && IsValidPoints (myFirstPoint,
1039                              myCenterPoint,
1040                              mySecondPoint);
1041   }
1042 }
1043
1044 //=======================================================================
1045 //function : InitConeAngle
1046 //purpose  : initialization of the cone angle
1047 //=======================================================================
1048 Standard_Boolean AIS_AngleDimension::InitConeAngle()
1049 {
1050   if (myFirstShape.IsNull())
1051   {
1052     return Standard_False;
1053   }
1054
1055   TopoDS_Face aConeShape = TopoDS::Face (myFirstShape);
1056   gp_Pln aPln;
1057   gp_Cone aCone;
1058   gp_Circ aCircle;
1059   // A surface from the Face
1060   Handle(Geom_Surface) aSurf;
1061   Handle(Geom_OffsetSurface) aOffsetSurf; 
1062   Handle(Geom_ConicalSurface) aConicalSurf;
1063   Handle(Geom_SurfaceOfRevolution) aRevSurf;
1064   Handle(Geom_Line) aLine;
1065   BRepAdaptor_Surface aConeAdaptor (aConeShape);
1066   TopoDS_Face aFace;
1067   AIS_KindOfSurface aSurfType;
1068   Standard_Real anOffset = 0.;
1069   Handle(Standard_Type) aType;
1070
1071   Standard_Real aMaxV = aConeAdaptor.FirstVParameter();
1072   Standard_Real aMinV = aConeAdaptor.LastVParameter();
1073
1074   AIS::GetPlaneFromFace (aConeShape, aPln, aSurf, aSurfType, anOffset);
1075
1076   if (aSurfType == AIS_KOS_Revolution)
1077   {
1078     // Surface of revolution
1079     aRevSurf = Handle(Geom_SurfaceOfRevolution)::DownCast(aSurf);
1080     gp_Lin aLin (aRevSurf->Axis());
1081     Handle(Geom_Curve) aBasisCurve = aRevSurf->BasisCurve();
1082     //Must be a part of line (basis curve should be linear)
1083     if (aBasisCurve ->DynamicType() != STANDARD_TYPE(Geom_Line))
1084       return Standard_False;
1085
1086     gp_Pnt aFirst1 = aConeAdaptor.Value (0., aMinV);
1087     gp_Pnt aLast1 = aConeAdaptor.Value (0., aMaxV);
1088     gp_Vec aVec1 (aFirst1, aLast1);
1089
1090     //Projection <aFirst> on <aLin>
1091     gp_Pnt aFirst2 = ElCLib::Value (ElCLib::Parameter (aLin, aFirst1), aLin);
1092     // Projection <aLast> on <aLin>
1093     gp_Pnt aLast2 = ElCLib::Value (ElCLib::Parameter (aLin, aLast1), aLin);
1094
1095     gp_Vec aVec2 (aFirst2, aLast2);
1096
1097     // Check if two parts of revolution are parallel (it's a cylinder) or normal (it's a circle).
1098     if (aVec1.IsParallel (aVec2, Precision::Angular())
1099         || aVec1.IsNormal (aVec2,Precision::Angular()))
1100       return Standard_False;
1101
1102     gce_MakeCone aMkCone (aRevSurf->Axis(), aFirst1, aLast1);
1103     aCone =  aMkCone.Value();
1104     myCenterPoint = aCone.Apex();
1105   }
1106   else
1107   {
1108     aType = aSurf->DynamicType();
1109     if (aType == STANDARD_TYPE(Geom_OffsetSurface) || anOffset > 0.01)
1110     {
1111       // Offset surface
1112       aOffsetSurf = new Geom_OffsetSurface (aSurf, anOffset);
1113       aSurf = aOffsetSurf->Surface();
1114       BRepBuilderAPI_MakeFace aMkFace(aSurf, Precision::Confusion());
1115       aMkFace.Build();
1116       if (!aMkFace.IsDone())
1117         return Standard_False;
1118       aConeAdaptor.Initialize (aMkFace.Face());
1119     }
1120     aCone = aConeAdaptor.Cone();
1121     aConicalSurf = Handle(Geom_ConicalSurface)::DownCast (aSurf);
1122     myCenterPoint =  aConicalSurf->Apex();
1123   }
1124
1125   // A circle where the angle is drawn
1126   Handle(Geom_Curve) aCurve;
1127   Standard_Real aMidV = ( aMinV + aMaxV ) / 2.5;
1128   aCurve = aSurf->VIso (aMidV);
1129   aCircle = Handle(Geom_Circle)::DownCast (aCurve)->Circ();
1130
1131   aCurve = aSurf->VIso(aMaxV);
1132   gp_Circ aCircVmax = Handle(Geom_Circle)::DownCast(aCurve)->Circ();
1133   aCurve = aSurf->VIso(aMinV);
1134   gp_Circ aCircVmin = Handle(Geom_Circle)::DownCast(aCurve)->Circ();
1135
1136   if (aCircVmax.Radius() < aCircVmin.Radius())
1137   {
1138    gp_Circ aTmpCirc = aCircVmax;
1139    aCircVmax = aCircVmin;
1140    aCircVmin = aTmpCirc;
1141   }
1142
1143   myFirstPoint  = ElCLib::Value (0, aCircle);
1144   mySecondPoint = ElCLib::Value (M_PI, aCircle);
1145   return Standard_True;
1146 }
1147
1148 //=======================================================================
1149 //function : IsValidPoints
1150 //purpose  : 
1151 //=======================================================================
1152 Standard_Boolean AIS_AngleDimension::IsValidPoints (const gp_Pnt& theFirstPoint,
1153                                                     const gp_Pnt& theCenterPoint,
1154                                                     const gp_Pnt& theSecondPoint) const
1155 {
1156   return theFirstPoint.Distance (theCenterPoint) > Precision::Confusion()
1157       && theSecondPoint.Distance (theCenterPoint) > Precision::Confusion()
1158       && gp_Vec (theCenterPoint, theFirstPoint).Angle (
1159            gp_Vec (theCenterPoint, theSecondPoint)) > Precision::Angular();
1160 }
1161
1162 //=======================================================================
1163 //function : GetTextPosition
1164 //purpose  : 
1165 //=======================================================================
1166 const gp_Pnt AIS_AngleDimension::GetTextPosition() const
1167 {
1168   if (!IsValid())
1169   {
1170     return gp::Origin();
1171   }
1172
1173   if (IsTextPositionCustom())
1174   {
1175     return myFixedTextPosition;
1176   }
1177
1178   // Counts text position according to the dimension parameters
1179   gp_Pnt aTextPosition (gp::Origin());
1180
1181   Handle(Prs3d_DimensionAspect) aDimensionAspect = myDrawer->DimensionAspect();
1182
1183   // Prepare label string and compute its geometrical width
1184   Standard_Real aLabelWidth;
1185   TCollection_ExtendedString aLabelString = GetValueString (aLabelWidth);
1186
1187   gp_Pnt aFirstAttach = myCenterPoint.Translated (gp_Vec(myCenterPoint, myFirstPoint).Normalized() * GetFlyout());
1188   gp_Pnt aSecondAttach = myCenterPoint.Translated (gp_Vec(myCenterPoint, mySecondPoint).Normalized() * GetFlyout());
1189
1190   // Handle user-defined and automatic arrow placement
1191   Standard_Boolean isArrowsExternal = Standard_False;
1192   Standard_Integer aLabelPosition = LabelPosition_None;
1193   FitTextAlignment (aDimensionAspect->TextHorizontalPosition(),
1194                     aLabelPosition, isArrowsExternal);
1195
1196   // Get text position
1197   switch (aLabelPosition & LabelPosition_HMask)
1198   {
1199   case LabelPosition_HCenter:
1200     {
1201       aTextPosition = GetCenterOnArc (aFirstAttach, aSecondAttach, myCenterPoint);
1202     }
1203     break;
1204   case LabelPosition_Left:
1205     {
1206       gp_Dir aPlaneNormal = gp_Vec (aFirstAttach, aSecondAttach) ^ gp_Vec (myCenterPoint, aFirstAttach);
1207       gp_Dir anExtensionDir = aPlaneNormal ^ gp_Vec (myCenterPoint, aFirstAttach);
1208       Standard_Real anExtensionSize = aDimensionAspect->ExtensionSize();
1209       Standard_Real anOffset = isArrowsExternal
1210           ? anExtensionSize + aDimensionAspect->ArrowAspect()->Length()
1211           : anExtensionSize;
1212       gp_Vec anExtensionVec  = gp_Vec (anExtensionDir) * -anOffset;
1213       aTextPosition = aFirstAttach.Translated (anExtensionVec);
1214     }
1215     break;
1216   case LabelPosition_Right:
1217     {
1218       gp_Dir aPlaneNormal = gp_Vec (aFirstAttach, aSecondAttach) ^ gp_Vec (myCenterPoint, aFirstAttach);
1219       gp_Dir anExtensionDir = aPlaneNormal ^ gp_Vec (myCenterPoint, aSecondAttach);
1220       Standard_Real anExtensionSize = aDimensionAspect->ExtensionSize();
1221       Standard_Real anOffset = isArrowsExternal
1222           ? anExtensionSize + aDimensionAspect->ArrowAspect()->Length()
1223           : anExtensionSize;
1224       gp_Vec anExtensionVec  = gp_Vec (anExtensionDir) * anOffset;
1225       aTextPosition = aSecondAttach.Translated (anExtensionVec);
1226     }
1227     break;
1228   }
1229
1230   return aTextPosition;
1231 }
1232
1233 //=======================================================================
1234 //function : SetTextPosition
1235 //purpose  : 
1236 //=======================================================================
1237 void AIS_AngleDimension::SetTextPosition (const gp_Pnt& theTextPos)
1238 {
1239   if (!IsValid())
1240   {
1241     return;
1242   }
1243
1244   // The text position point for angle dimension should belong to the working plane.
1245   if (!GetPlane().Contains (theTextPos, Precision::Confusion()))
1246   {
1247     Standard_ProgramError::Raise ("The text position point for angle dimension doesn't belong to the working plane.");
1248   }
1249
1250   myIsTextPositionFixed = Standard_True;
1251   myFixedTextPosition = theTextPos;
1252 }
1253
1254 //=======================================================================
1255 //function : AdjustParameters
1256 //purpose  : 
1257 //=======================================================================
1258 void AIS_AngleDimension::AdjustParameters (const gp_Pnt& theTextPos,
1259                                            Standard_Real& theExtensionSize,
1260                                            Prs3d_DimensionTextHorizontalPosition& theAlignment,
1261                                            Standard_Real& theFlyout) const
1262 {
1263   Handle(Prs3d_DimensionAspect) aDimensionAspect = myDrawer->DimensionAspect();
1264   Standard_Real anArrowLength = aDimensionAspect->ArrowAspect()->Length();
1265
1266   // Build circle with radius that is equal to distance from text position to the center point.
1267   Standard_Real aRadius = gp_Vec (myCenterPoint, theTextPos).Magnitude();
1268
1269   // Set attach points in positive direction of the flyout.
1270   gp_Pnt aFirstAttach = myCenterPoint.Translated (gp_Vec (myCenterPoint, myFirstPoint).Normalized() * aRadius);
1271   gp_Pnt aSecondAttach = myCenterPoint.Translated (gp_Vec (myCenterPoint, mySecondPoint).Normalized() * aRadius);
1272
1273   gce_MakeCirc aConstructCircle (myCenterPoint, GetPlane(), aRadius);
1274   if (!aConstructCircle.IsDone())
1275   {
1276     return;
1277   }
1278   gp_Circ aCircle = aConstructCircle.Value();
1279
1280   // Default values
1281   theExtensionSize = aDimensionAspect->ArrowAspect()->Length();
1282   theAlignment = Prs3d_DTHP_Center;
1283
1284   Standard_Real aParamBeg = ElCLib::Parameter (aCircle, aFirstAttach);
1285   Standard_Real aParamEnd = ElCLib::Parameter (aCircle, aSecondAttach);
1286   if (aParamEnd < aParamBeg)
1287   {
1288     Standard_Real aParam = aParamEnd;
1289     aParamEnd = aParamBeg;
1290     aParamBeg = aParam;
1291   }
1292
1293   ElCLib::AdjustPeriodic (0.0, M_PI * 2, Precision::PConfusion(), aParamBeg, aParamEnd);
1294   Standard_Real aTextPar = ElCLib::Parameter (aCircle , theTextPos);
1295
1296   // Horizontal center
1297   if (aTextPar > aParamBeg && aTextPar < aParamEnd)
1298   {
1299     theFlyout = aRadius;
1300     return;
1301   }
1302
1303   aParamBeg += M_PI;
1304   aParamEnd += M_PI;
1305   ElCLib::AdjustPeriodic (0.0, M_PI * 2, Precision::PConfusion(), aParamBeg, aParamEnd);
1306
1307   if (aTextPar > aParamBeg  && aTextPar < aParamEnd)
1308   {
1309     theFlyout = -aRadius;
1310     return;
1311   }
1312
1313   // Text on the extensions
1314   gp_Lin aFirstLine = gce_MakeLin (myCenterPoint, myFirstPoint);
1315   gp_Lin aSecondLine = gce_MakeLin (myCenterPoint, mySecondPoint);
1316   gp_Pnt aFirstTextProj = AIS::Nearest (aFirstLine, theTextPos);
1317   gp_Pnt aSecondTextProj = AIS::Nearest (aSecondLine, theTextPos);
1318   Standard_Real aFirstDist = aFirstTextProj.Distance (theTextPos);
1319   Standard_Real aSecondDist = aSecondTextProj.Distance (theTextPos);
1320
1321   if (aFirstDist <= aSecondDist)
1322   {
1323     aRadius = myCenterPoint.Distance (aFirstTextProj);
1324     Standard_Real aNewExtensionSize = aFirstDist - anArrowLength;
1325     theExtensionSize = aNewExtensionSize < 0.0 ? 0.0 : aNewExtensionSize;
1326
1327     theAlignment = Prs3d_DTHP_Left;
1328
1329     gp_Vec aPosFlyoutDir = gp_Vec (myCenterPoint, myFirstPoint).Normalized().Scaled (aRadius);
1330
1331     theFlyout = aFirstTextProj.Distance (myCenterPoint.Translated (aPosFlyoutDir)) > Precision::Confusion()
1332                 ? -aRadius : aRadius;
1333   }
1334   else
1335   {
1336     aRadius = myCenterPoint.Distance (aSecondTextProj);
1337
1338     Standard_Real aNewExtensionSize = aSecondDist - anArrowLength;
1339
1340     theExtensionSize = aNewExtensionSize < 0.0 ? 0.0 : aNewExtensionSize;
1341
1342     theAlignment = Prs3d_DTHP_Right;
1343
1344     gp_Vec aPosFlyoutDir = gp_Vec (myCenterPoint, mySecondPoint).Normalized().Scaled (aRadius);
1345
1346     theFlyout = aSecondTextProj.Distance (myCenterPoint.Translated (aPosFlyoutDir)) > Precision::Confusion()
1347                 ? -aRadius : aRadius;
1348   }
1349 }
1350
1351 //=======================================================================
1352 //function : FitTextAlignment
1353 //purpose  : 
1354 //=======================================================================
1355 void AIS_AngleDimension::FitTextAlignment (const Prs3d_DimensionTextHorizontalPosition& theHorizontalTextPos,
1356                                            Standard_Integer& theLabelPosition,
1357                                            Standard_Boolean& theIsArrowsExternal) const
1358 {
1359   Handle(Prs3d_DimensionAspect) aDimensionAspect = myDrawer->DimensionAspect();
1360
1361   Quantity_Length anArrowLength = aDimensionAspect->ArrowAspect()->Length();
1362
1363   // Prepare label string and compute its geometrical width
1364   Standard_Real aLabelWidth;
1365   TCollection_ExtendedString aLabelString = GetValueString (aLabelWidth);
1366
1367   // add margins to label width
1368   if (aDimensionAspect->IsText3d())
1369   {
1370     aLabelWidth += aDimensionAspect->TextAspect()->Height() * THE_3D_TEXT_MARGIN * 2.0;
1371   }
1372
1373   gp_Pnt aFirstAttach = myCenterPoint.Translated (gp_Vec (myCenterPoint, myFirstPoint).Normalized() * GetFlyout());
1374   gp_Pnt aSecondAttach = myCenterPoint.Translated (gp_Vec (myCenterPoint, mySecondPoint).Normalized() * GetFlyout());
1375
1376   // Handle user-defined and automatic arrow placement
1377   switch (aDimensionAspect->ArrowOrientation())
1378   {
1379     case Prs3d_DAO_External: theIsArrowsExternal = true; break;
1380     case Prs3d_DAO_Internal: theIsArrowsExternal = false; break;
1381     case Prs3d_DAO_Fit:
1382     {
1383       gp_Vec anAttachVector (aFirstAttach, aSecondAttach);
1384       Standard_Real aDimensionWidth = anAttachVector.Magnitude();
1385
1386       // Add margin to ensure a small tail between text and arrow
1387       Standard_Real anArrowMargin   = aDimensionAspect->IsText3d() 
1388                                     ? aDimensionAspect->TextAspect()->Height() * THE_3D_TEXT_MARGIN
1389                                     : 0.0;
1390
1391       Standard_Real anArrowsWidth   = (anArrowLength + anArrowMargin) * 2.0;
1392
1393       theIsArrowsExternal = aDimensionWidth < aLabelWidth + anArrowsWidth;
1394       break;
1395     }
1396   }
1397
1398   // Handle user-defined and automatic text placement
1399   switch (theHorizontalTextPos)
1400   {
1401     case Prs3d_DTHP_Left  : theLabelPosition |= LabelPosition_Left; break;
1402     case Prs3d_DTHP_Right : theLabelPosition |= LabelPosition_Right; break;
1403     case Prs3d_DTHP_Center: theLabelPosition |= LabelPosition_HCenter; break;
1404     case Prs3d_DTHP_Fit:
1405     {
1406       gp_Vec anAttachVector (aFirstAttach, aSecondAttach);
1407       Standard_Real aDimensionWidth = anAttachVector.Magnitude();
1408       Standard_Real anArrowsWidth   = anArrowLength * 2.0;
1409       Standard_Real aContentWidth   = theIsArrowsExternal ? aLabelWidth : aLabelWidth + anArrowsWidth;
1410
1411       theLabelPosition |= aDimensionWidth < aContentWidth ? LabelPosition_Left : LabelPosition_HCenter;
1412       break;
1413     }
1414   }
1415
1416   switch (aDimensionAspect->TextVerticalPosition())
1417   {
1418     case Prs3d_DTVP_Above  : theLabelPosition |= LabelPosition_Above; break;
1419     case Prs3d_DTVP_Below  : theLabelPosition |= LabelPosition_Below; break;
1420     case Prs3d_DTVP_Center : theLabelPosition |= LabelPosition_VCenter; break;
1421   }
1422 }