0029938: Visualization - SelectMgr_ViewerSelector::PickedPoint() should return point...
[occt.git] / src / Select3D / Select3D_SensitiveCircle.cxx
1 // Created on: 1996-02-06
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 #include <Select3D_SensitiveCircle.hxx>
18
19 #include <Geom_Circle.hxx>
20 #include <Precision.hxx>
21 #include <Select3D_SensitiveTriangle.hxx>
22
23 IMPLEMENT_STANDARD_RTTIEXT(Select3D_SensitiveCircle,Select3D_SensitivePoly)
24
25 namespace
26 {
27   static Standard_Integer GetCircleNbPoints (const Handle(Geom_Circle)& theCircle,
28                                              const Standard_Integer theNbPnts)
29   {
30     // Check if number of points is invalid.
31     // In this case myPolyg raises Standard_ConstructionError
32     // exception (look constructor bellow).
33     if (theNbPnts <= 0)
34       return 0;
35
36     if (theCircle->Radius() > Precision::Confusion())
37       return 2 * theNbPnts + 1;
38
39     // The radius is too small and circle degenerates into point
40     return 1;
41   }
42
43   static Standard_Integer GetArcNbPoints (const Handle(Geom_Circle)& theCircle,
44                                           const Standard_Integer theNbPnts)
45   {
46     // There is no need to check number of points here.
47     // In case of invalid number of points this method returns
48     // -1 or smaller value.
49     if (theCircle->Radius() > Precision::Confusion())
50       return 2 * theNbPnts - 1;
51
52     // The radius is too small and circle degenerates into point
53     return 1;
54   }
55 }
56
57 //=======================================================================
58 //function : Select3D_SensitiveCircle (constructor)
59 //purpose  : Definition of a sensitive circle
60 //=======================================================================
61 Select3D_SensitiveCircle::Select3D_SensitiveCircle(const Handle(SelectBasics_EntityOwner)& theOwnerId,
62                                                    const Handle(Geom_Circle)& theCircle,
63                                                    const Standard_Boolean theIsFilled,
64                                                    const Standard_Integer theNbPnts)
65 : Select3D_SensitivePoly (theOwnerId, !theIsFilled, GetCircleNbPoints (theCircle, theNbPnts)),
66   myCircle (theCircle),
67   myStart (0),
68   myEnd (0)
69 {
70   mySensType = theIsFilled ? Select3D_TOS_INTERIOR : Select3D_TOS_BOUNDARY;
71   if (myPolyg.Size() != 1)
72   {
73     gp_Pnt aP1, aP2;
74     gp_Vec aV1;
75     Standard_Real anUStart = theCircle->FirstParameter();
76     Standard_Real anUEnd = theCircle->LastParameter();
77     Standard_Real aStep = (anUEnd - anUStart) / theNbPnts;
78     Standard_Real aRadius = theCircle->Radius();
79     Standard_Integer aPntIdx = 1;
80     Standard_Real aCurU = anUStart;
81     for (Standard_Integer anIndex = 1; anIndex <= theNbPnts; anIndex++)
82     {
83       theCircle->D1 (aCurU, aP1, aV1);
84
85       aV1.Normalize();
86       myPolyg.SetPnt (aPntIdx - 1, aP1);
87       aPntIdx++;
88       aP2 = gp_Pnt (aP1.X() + aV1.X() * tan (aStep / 2.0) * aRadius,
89                     aP1.Y() + aV1.Y() * tan (aStep / 2.0) * aRadius,
90                     aP1.Z() + aV1.Z() * tan (aStep / 2.0) * aRadius);
91       myPolyg.SetPnt (aPntIdx - 1, aP2);
92       aPntIdx++;
93       aCurU += aStep;
94     }
95
96     // Copy the first point to the last point of myPolyg
97     myPolyg.SetPnt (theNbPnts * 2, myPolyg.Pnt (0));
98     // Get myCenter3D
99     myCenter3D = theCircle->Location();
100   }
101   // Radius = 0.0
102   else
103   {
104     myPolyg.SetPnt (0, theCircle->Location());
105     // Get myCenter3D
106     myCenter3D = myPolyg.Pnt (0);
107   }
108
109   if (mySensType == Select3D_TOS_BOUNDARY)
110   {
111     SetSensitivityFactor (6);
112   }
113 }
114
115 //=======================================================================
116 //function : Select3D_SensitiveCircle (constructor)
117 //purpose  : Definition of a sensitive arc
118 //=======================================================================
119 Select3D_SensitiveCircle::Select3D_SensitiveCircle (const Handle(SelectBasics_EntityOwner)& theOwnerId,
120                                                     const Handle(Geom_Circle)& theCircle,
121                                                     const Standard_Real theU1,
122                                                     const Standard_Real theU2,
123                                                     const Standard_Boolean theIsFilled,
124                                                     const Standard_Integer theNbPnts)
125 : Select3D_SensitivePoly (theOwnerId, !theIsFilled, GetArcNbPoints (theCircle, theNbPnts)),
126   myCircle (theCircle),
127   myStart (Min (theU1, theU2)),
128   myEnd (Max (theU1, theU2))
129 {
130   mySensType = theIsFilled ? Select3D_TOS_INTERIOR : Select3D_TOS_BOUNDARY;
131
132   if (myPolyg.Size() != 1)
133   {
134     gp_Pnt aP1, aP2;
135     gp_Vec aV1;
136
137     Standard_Real aStep = (myEnd - myStart) / (theNbPnts - 1);
138     Standard_Real aRadius = theCircle->Radius();
139     Standard_Integer aPntIdx = 1;
140     Standard_Real aCurU = myStart;
141
142     for (Standard_Integer anIndex = 1; anIndex <= theNbPnts - 1; anIndex++)
143     {
144       theCircle->D1 (aCurU, aP1, aV1);
145       aV1.Normalize();
146       myPolyg.SetPnt (aPntIdx - 1, aP1);
147       aPntIdx++;
148       aP2 = gp_Pnt (aP1.X() + aV1.X() * tan (aStep /2.0) * aRadius,
149                     aP1.Y() + aV1.Y() * tan (aStep /2.0) * aRadius,
150                     aP1.Z() + aV1.Z() * tan (aStep /2.0) * aRadius);
151       myPolyg.SetPnt (aPntIdx - 1, aP2);
152       aPntIdx++;
153       aCurU += aStep;
154     }
155     theCircle->D0 (myEnd, aP1);
156     myPolyg.SetPnt (theNbPnts * 2 - 2, aP1);
157     // Get myCenter3D
158     myCenter3D = theCircle->Location();
159   }
160   else
161   {
162     myPolyg.SetPnt (0, theCircle->Location());
163     // Get myCenter3D
164     myCenter3D = myPolyg.Pnt (0);
165   }
166
167   if (mySensType == Select3D_TOS_BOUNDARY)
168   {
169     SetSensitivityFactor (6);
170   }
171 }
172
173 //=======================================================================
174 //function : Select3D_SensitiveCircle
175 //purpose  :
176 //=======================================================================
177 Select3D_SensitiveCircle::Select3D_SensitiveCircle(const Handle(SelectBasics_EntityOwner)& theOwnerId,
178                                                    const Handle(TColgp_HArray1OfPnt)& thePnts3d,
179                                                    const Standard_Boolean theIsFilled)
180 : Select3D_SensitivePoly (theOwnerId, thePnts3d, static_cast<Standard_Boolean> (!theIsFilled)),
181   myStart (0),
182   myEnd (0)
183 {
184   mySensType = theIsFilled ? Select3D_TOS_INTERIOR : Select3D_TOS_BOUNDARY;
185
186   if (myPolyg.Size() != 1)
187     computeCenter3D();
188   else
189     myCenter3D = myPolyg.Pnt (0);
190
191   if (mySensType == Select3D_TOS_BOUNDARY)
192   {
193     SetSensitivityFactor (6);
194   }
195 }
196
197 //=======================================================================
198 //function : Select3D_SensitiveCircle
199 //purpose  :
200 //=======================================================================
201
202 Select3D_SensitiveCircle::Select3D_SensitiveCircle(const Handle(SelectBasics_EntityOwner)& theOwnerId,
203                                                    const TColgp_Array1OfPnt& thePnts3d,
204                                                    const Standard_Boolean theIsFilled)
205 : Select3D_SensitivePoly (theOwnerId, thePnts3d, !theIsFilled),
206   myStart (0),
207   myEnd (0)
208 {
209   mySensType = theIsFilled ? Select3D_TOS_INTERIOR : Select3D_TOS_BOUNDARY;
210
211   if (myPolyg.Size() != 1)
212     computeCenter3D();
213   else
214     myCenter3D = myPolyg.Pnt (0);
215
216   if (mySensType == Select3D_TOS_BOUNDARY)
217   {
218     SetSensitivityFactor (6);
219   }
220 }
221
222 //=======================================================================
223 // function : BVH
224 // purpose  : Builds BVH tree for a circle's edge segments if needed
225 //=======================================================================
226 void Select3D_SensitiveCircle::BVH()
227 {
228   if (mySensType == Select3D_TOS_BOUNDARY)
229   {
230     Select3D_SensitivePoly::BVH();
231   }
232 }
233
234 //=======================================================================
235 // function : Matches
236 // purpose  : Checks whether the circle overlaps current selecting volume
237 //=======================================================================
238 Standard_Boolean Select3D_SensitiveCircle::Matches (SelectBasics_SelectingVolumeManager& theMgr,
239                                                     SelectBasics_PickResult& thePickResult)
240 {
241   if (mySensType == Select3D_TOS_BOUNDARY)
242   {
243     if (!Select3D_SensitivePoly::Matches (theMgr, thePickResult))
244     {
245       return Standard_False;
246     }
247   }
248   else if (mySensType == Select3D_TOS_INTERIOR)
249   {
250     Handle(TColgp_HArray1OfPnt) anArrayOfPnt;
251     Points3D (anArrayOfPnt);
252     if (!theMgr.IsOverlapAllowed())
253     {
254       for (Standard_Integer aPntIdx = anArrayOfPnt->Lower(); aPntIdx <= anArrayOfPnt->Upper(); ++aPntIdx)
255       {
256         if (!theMgr.Overlaps (anArrayOfPnt->Value(aPntIdx)))
257         {
258           return Standard_False;
259         }
260       }
261       return Standard_True;
262     }
263
264     if (!theMgr.Overlaps (anArrayOfPnt, Select3D_TOS_INTERIOR, thePickResult))
265     {
266       return Standard_False;
267     }
268     thePickResult.SetDistToGeomCenter(distanceToCOG(theMgr));
269   }
270
271   return Standard_True;
272 }
273
274 void Select3D_SensitiveCircle::ArrayBounds (Standard_Integer & theLow,
275                                             Standard_Integer & theUp) const
276 {
277     theLow = 0;
278     theUp = myPolyg.Size() - 1;
279 }
280
281 //=======================================================================
282 //function : GetPoint3d
283 //purpose  :
284 //=======================================================================
285 gp_Pnt Select3D_SensitiveCircle::GetPoint3d (const Standard_Integer thePntIdx) const
286 {
287   if (thePntIdx >= 0 && thePntIdx < myPolyg.Size())
288     return myPolyg.Pnt (thePntIdx);
289
290   return gp_Pnt();
291 }
292
293 //=======================================================================
294 //function : GetConnected
295 //purpose  :
296 //=======================================================================
297
298 Handle(Select3D_SensitiveEntity) Select3D_SensitiveCircle::GetConnected()
299 {
300   Standard_Boolean isFilled = mySensType == Select3D_TOS_INTERIOR;
301   // Create a copy of this
302   Handle(Select3D_SensitiveEntity) aNewEntity;
303   // this was constructed using Handle(Geom_Circle)
304   if(!myCircle.IsNull())
305   {
306     if ((myEnd - myStart) > Precision::Confusion())
307     {
308       // Arc
309       aNewEntity = new Select3D_SensitiveCircle (myOwnerId, myCircle, myStart, myEnd, isFilled);
310     }
311     else
312     {
313       // Circle
314       aNewEntity = new Select3D_SensitiveCircle (myOwnerId, myCircle, isFilled);
315     }
316   }
317   // this was constructed using TColgp_Array1OfPnt
318   else
319   {
320     Standard_Integer aSize = myPolyg.Size();
321     TColgp_Array1OfPnt aPolyg (1, aSize);
322     for(Standard_Integer anIndex = 1; anIndex <= aSize; ++anIndex)
323     {
324       aPolyg.SetValue(anIndex, myPolyg.Pnt (anIndex-1));
325     }
326     aNewEntity = new Select3D_SensitiveCircle (myOwnerId, aPolyg, isFilled);
327   }
328
329   return aNewEntity;
330 }
331
332 //=======================================================================
333 //function : computeCenter3D
334 //purpose  :
335 //=======================================================================
336 void Select3D_SensitiveCircle::computeCenter3D()
337 {
338   gp_XYZ aCenter;
339   Standard_Integer aNbPnts = myPolyg.Size();
340   if (aNbPnts != 1)
341   {
342     // The mass of points system
343     Standard_Integer aMass = aNbPnts - 1;
344     // Find the circle barycenter
345     for (Standard_Integer anIndex = 0; anIndex < aNbPnts - 1; ++anIndex)
346     {
347       aCenter += myPolyg.Pnt(anIndex);
348     }
349     myCenter3D = aCenter / aMass;
350   }
351   else
352   {
353     myCenter3D = myPolyg.Pnt(0);
354   }
355 }
356
357 //=======================================================================
358 // function : CenterOfGeometry
359 // purpose  : Returns center of the circle. If location transformation
360 //            is set, it will be applied
361 //=======================================================================
362 gp_Pnt Select3D_SensitiveCircle::CenterOfGeometry() const
363 {
364   return myCenter3D;
365 }
366
367 //=======================================================================
368 // function : distanceToCOG
369 // purpose  :
370 //=======================================================================
371 Standard_Real Select3D_SensitiveCircle::distanceToCOG (SelectBasics_SelectingVolumeManager& theMgr)
372 {
373   return theMgr.DistToGeometryCenter (myCenter3D);
374 }