1 // File: BRepMesh_Classifier.cxx
2 // Created: Thu Jun 26 14:54:02 1997
3 // Author: Laurent PAINNOT
4 // <lpa@penox.paris1.matra-dtv.fr>
7 #include <BRepMesh_Classifier.ixx>
10 #include <Precision.hxx>
11 #include <Standard_ErrorHandler.hxx>
12 #include <TColStd_ListOfTransient.hxx>
13 #include <TColStd_Array1OfInteger.hxx>
14 #include <TColStd_DataMapOfIntegerInteger.hxx>
18 #include <gp_Pnt2d.hxx>
19 #include <TColgp_SequenceOfPnt2d.hxx>
20 #include <TColgp_Array1OfPnt2d.hxx>
21 #include <GeomAbs_SurfaceType.hxx>
22 #include <Geom2dInt_Geom2dCurveTool.hxx>
23 #include <Geom2d_Line.hxx>
24 #include <Geom2d_BezierCurve.hxx>
25 #include <Geom2d_BSplineCurve.hxx>
26 #include <Geom2d_TrimmedCurve.hxx>
28 #include <BRep_Tool.hxx>
29 #include <BRepTools.hxx>
30 #include <BRepTools_WireExplorer.hxx>
31 #include <BRepAdaptor_Curve2d.hxx>
32 #include <TopAbs_Orientation.hxx>
34 #include <TopExp_Explorer.hxx>
35 #include <TopoDS_Edge.hxx>
37 #include <CSLib_Class2d.hxx>
38 #include <Poly_PolygonOnTriangulation.hxx>
40 #include <BRepMesh_Vertex.hxx>
41 #include <BRepMesh_Array1OfBiPoint.hxx>
42 #include <BRepMesh_PairOfPolygon.hxx>
45 static Standard_Integer debwire;
46 static Standard_Integer debedge;
47 static Standard_Integer debclass = 0;
49 static const Standard_Real MIN_DIST = 2.E-5; //EPA: real mesh is created in the grid 10E5x10E5, so intersection should be cheched
50 // with double of discretization.
51 static const Standard_Real PARALL_COND = Sin(M_PI/3.0);
52 static const Standard_Real RESOLUTION = 1.0E-16; //OCC319
55 //=======================================================================
58 //=======================================================================
60 static Standard_Boolean IsLine(const Handle(Geom2d_Curve)& C2d)
62 Standard_Boolean IsALine = Standard_False;
63 if ( C2d->IsKind(STANDARD_TYPE(Geom2d_Line)) )
65 IsALine = Standard_True;
67 else if ( C2d->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) )
69 Handle(Geom2d_BSplineCurve) BS = *((Handle(Geom2d_BSplineCurve)*)&C2d);
70 IsALine = (BS->NbPoles() == 2);
72 else if ( C2d->IsKind(STANDARD_TYPE(Geom2d_BezierCurve)) )
74 Handle(Geom2d_BezierCurve) Bz = *((Handle(Geom2d_BezierCurve)*)&C2d);
75 IsALine = (Bz->NbPoles() == 2);
77 else if ( C2d->IsKind(STANDARD_TYPE(Geom2d_TrimmedCurve)) )
79 Handle(Geom2d_TrimmedCurve) Curv = *((Handle(Geom2d_TrimmedCurve)*)&C2d);
80 IsALine = IsLine(Curv->BasisCurve());
85 //=======================================================================
86 //function : AnalizeWire
88 //=======================================================================
90 void BRepMesh_Classifier::AnalizeWire (const TColgp_SequenceOfPnt2d& theSeqPnt2d,
91 const Standard_Real Umin, const Standard_Real Umax,
92 const Standard_Real Vmin, const Standard_Real Vmax)
94 const Standard_Integer nbpnts = theSeqPnt2d.Length();
95 if (nbpnts < 2) return;
98 TColgp_Array1OfPnt2d PClass(1,nbpnts);
99 Standard_Integer i, ii;
100 Standard_Real theangle = 0.0;
101 gp_Pnt2d p1 = theSeqPnt2d(1), p2 = theSeqPnt2d(2), p3;
104 for (i = 1; i <= nbpnts; i++)
109 p3 = PClass(ii-nbpnts);
113 p3 = theSeqPnt2d.Value(ii);
116 gp_Vec2d A(p1,p2), B(p2,p3);
117 if (A.SquareMagnitude() > 1.e-16 && B.SquareMagnitude() > 1.e-16)
119 const Standard_Real a = A.Angle(B);
120 const Standard_Real aa = Abs(a);
121 // Check if vectors are opposite
122 if (aa > Precision::Angular() && (M_PI - aa) > Precision::Angular())
130 // Check for zero angle - treat self intersecting wire as outer
131 if (Abs(theangle) < Precision::Angular()) theangle = 0.0;
133 TabClass.Append((void *)new CSLib_Class2d(PClass,Toluv,Toluv,Umin,Vmin,Umax,Vmax));
134 TabOrien.Append((theangle < 0.0) ? 0 : 1);
137 //=======================================================================
138 //function : triangle2Area
139 //purpose : calculating area under triangle
140 //=======================================================================
142 inline static Standard_Real triangle2Area(const gp_XY& p1, const gp_XY& p2)
144 return p1.Crossed(p2);
147 //=======================================================================
148 //function : getSegmentParams
149 //purpose : extracting segment attributes
150 //=======================================================================
152 static Standard_Real getSegmentParams(const BRepMesh_Array1OfBiPoint& theBiPoints,
153 const Standard_Integer Index,
162 Standard_Real *aCoordinates;
163 aCoordinates = ((Standard_Real*)(theBiPoints(Index).Coordinates()));
164 x11 = aCoordinates[0];
165 y11 = aCoordinates[1];
166 x12 = aCoordinates[2];
167 y12 = aCoordinates[3];
169 B = -aCoordinates[4];
174 //=======================================================================
175 //function : checkWiresIntersection
176 //purpose : finding intersection.
177 // If the intersection is found return Standard_True
178 //=======================================================================
180 static Standard_Boolean checkWiresIntersection(const Standard_Integer theFirstWireId,
181 const Standard_Integer theSecondWireId,
182 Standard_Integer* const theFirstOuterSegmentId,
183 Standard_Integer theLastOuterSegmentId,
184 const TColStd_SequenceOfInteger& theWireLength,
185 const BRepMesh_Array1OfBiPoint& theBiPoints,
186 const Standard_Boolean findNextIntersection = Standard_False,
187 const Standard_Boolean isFirstSegment = Standard_False,
188 Standard_Integer* const theFirstInnerSegmentId = 0)
190 Standard_Real A1, B1, C1, A2, B2, C2, AB, BC, CA, xc, yc;
191 Standard_Real mu1, d, mu2;
192 Standard_Integer ik = *theFirstOuterSegmentId, jk;
193 Standard_Real x11, x12, y11, y12, x21, x22, y21, y22;
195 // Calculate bounds for first wire
196 Standard_Integer ikEnd = theLastOuterSegmentId;
197 Standard_Boolean isFirst = Standard_True;
198 if ( findNextIntersection )
200 isFirst = isFirstSegment;
203 // Calculate bounds for second wire
204 Standard_Integer jkStart = 0, jkEnd = 0;
205 for (jk = 1; jk <= theSecondWireId; jk++)
208 jkEnd += theWireLength(jk);
211 // total area under polygon (area of loop)
212 Standard_Real aLoopArea = 0.0;
213 // area under first triangles of polygon
214 Standard_Real aFirstTriangleArea = 0.0;
215 // contains coordinates of the end point of segment if first intersection point is finding
216 // or coordinates of the intersecting point if second intersection point is finding
219 for (; ik <= ikEnd; ik++)
221 mu1 = getSegmentParams(theBiPoints, ik, x11, y11, x12, y12, A1, B1, C1);
222 // for second intersection point we must count the area from first intersection point
223 if ( !findNextIntersection )
226 aStartPoint.SetCoord(x12, y12);
229 //for theFirstWireId == theSecondWireId the algorithm check current wire on selfintersection
230 if ( findNextIntersection && theFirstInnerSegmentId && isFirst)
232 jk = *theFirstInnerSegmentId;
234 else if (theSecondWireId == theFirstWireId)
243 // Explore second wire
244 Standard_Boolean aFirstPass = Standard_True;
245 for (; jk <= jkEnd; jk++)
247 // don't check end's segment of the wire on selfrestriction
248 if ( theSecondWireId == theFirstWireId && isFirst && jk == ikEnd ) continue;
250 mu2 = getSegmentParams(theBiPoints, jk, x21, y21, x22, y22, A2, B2, C2);
251 gp_XY p2(x21, y21), p3(x22, y22);
253 //different segments may have common vertex (see OCC287 bug for example)
255 //check on minimal of distance between current segment and points of another linear segments - OCC319
256 d = A1*x22 + B1*y22 + C1;
257 Standard_Real dTol = MIN_DIST*MIN_DIST;
258 if(theFirstWireId != theSecondWireId && // if compared wires are different &&
259 AB*AB > PARALL_COND*PARALL_COND*mu1*mu2 && // angle between two segments greater then PARALL_COND &&
260 d*d < dTol*mu1 && // distance between vertex of the segment and other one's less then MIN_DIST
261 (x22-x11)*(x22-x12) < 0.0 && (y22-y11)*(y22-y12) < 0.0)
263 // if we finding the second intersection we must return Standard_False for setting
264 // self-intersection result flag
265 if ( findNextIntersection )
266 return Standard_False;
268 // we can step here when finding first intersection, return self-intersection flag
269 return Standard_True;
274 aFirstTriangleArea = triangle2Area(aStartPoint, p2);
276 Standard_Real aTmpArea = triangle2Area(p2, p3);
278 //look for intersection of two linear segments
279 if(Abs(AB) <= RESOLUTION)
281 aLoopArea += aTmpArea;
282 continue; //current segments seem parallel - no intersection
285 //calculate coordinates of point of the intersection
286 BC = B1*C2 - B2*C1; xc = BC/AB;
287 CA = C1*A2 - C2*A1; yc = CA/AB;
289 // remember current intersection point and area of first triangle
290 if( findNextIntersection && ik == *theFirstOuterSegmentId && jk == *theFirstInnerSegmentId )
292 aStartPoint.SetCoord(xc, yc);
296 //check on belonging of intersection point to the both of segments
297 Standard_Boolean isOnLines = Standard_True;
299 Standard_Real dd[2][4] = { {(xc-x11), (xc-x12), (xc-x21), (xc-x22)}, //dX
300 {(yc-y11), (yc-y12), (yc-y21), (yc-y22)} }; //dY
302 Standard_Integer i = 0;
305 if ( dd[i][0]*dd[i][1] > dTol || dd[i][2]*dd[i][3] > dTol)
307 isOnLines = Standard_False;
312 // check the intersection point is on the ends of segments
315 for( i = 0; i < 2; i++ )
317 // if it's the last segment and intersection point lies at the end
318 if ( ( jk == jkEnd ||
320 // or when the start or the end point of the first segment
321 (Abs(dd[0][0]) < MIN_DIST && Abs(dd[1][0]) < MIN_DIST) ||
322 (Abs(dd[0][1]) < MIN_DIST && Abs(dd[1][1]) < MIN_DIST)) &&
323 // is equal to one of the end points of the second
324 (Abs(dd[0][i+2]) < MIN_DIST && Abs(dd[1][i+2]) < MIN_DIST))
327 isOnLines = Standard_False;
328 aLoopArea = aTmpArea = 0.0;
329 aFirstPass = Standard_True;
338 p3.SetX(xc); p3.SetY(yc);
339 aLoopArea += aFirstTriangleArea; // First triangle area
340 aLoopArea += triangle2Area(p2, p3);
341 aLoopArea += triangle2Area(p3, aStartPoint); // Last triangle area
343 if( Abs(aLoopArea)/2 > M_PI*MIN_DIST )
345 if ( findNextIntersection )
347 // intersection is found, but Standard_False returns, because area is too much
348 return Standard_False;
351 if ( checkWiresIntersection(theFirstWireId, theSecondWireId, &ik, ikEnd, theWireLength,
352 theBiPoints, Standard_True, isFirst, &jk) )
354 // small crossing is not intersection, continue cheching
355 aLoopArea = aTmpArea = 0.0;
356 aFirstPass = Standard_True;
360 // if we found only one intersection
361 return Standard_True;
364 else if ( findNextIntersection )
366 // small intersection, skip double checking
367 *theFirstOuterSegmentId = ik;
368 *theFirstInnerSegmentId = jk + 1;
369 return Standard_True;
374 aFirstPass = Standard_False;
377 aLoopArea += aTmpArea;
382 isFirst = Standard_False;
385 return Standard_False;
389 //=======================================================================
390 //function : BRepMesh_Classifier
392 //=======================================================================
394 BRepMesh_Classifier::BRepMesh_Classifier(const TopoDS_Face& aFace,
395 const Standard_Real TolUV,
396 const BRepMesh_DataMapOfShapePairOfPolygon& edges,
397 const TColStd_IndexedMapOfInteger& themap,
398 const Handle(BRepMesh_DataStructureOfDelaun)& Str,
399 const Standard_Real Umin,
400 const Standard_Real Umax,
401 const Standard_Real Vmin,
402 const Standard_Real Vmax):
403 Toluv(TolUV), Face(aFace),
404 myState(BRepMesh_NoError)
406 //-- impasse sur les surfs definies sur plus d une periode
409 Face.Orientation(TopAbs_FORWARD);
412 BRepTools_WireExplorer WireExplorer;
413 //TopExp_Explorer FaceExplorer;
414 TopoDS_Iterator FaceExplorer;
416 TColgp_SequenceOfPnt2d aWirePoints, aWire;
417 TColStd_SequenceOfInteger aWireLength;
420 //-- twice definitions
421 TopAbs_Orientation anOr = TopAbs_FORWARD;
422 Standard_Boolean falsewire = Standard_False;
423 Standard_Integer i, index, firstindex = 0, lastindex = 0, nbedges = 0;
428 for(FaceExplorer.Initialize(Face); FaceExplorer.More(); FaceExplorer.Next())
430 if( FaceExplorer.Value().ShapeType()!= TopAbs_WIRE)
433 if (debclass) { debwire++; cout <<endl; cout << "#wire no "<<debwire; debedge = 0;}
435 // For each wire we create a data map, linking vertices (only
436 // the ends of edges) with their positions in the sequence of
437 // all 2d points from this wire.
438 // When we meet some vertex for the second time - the piece
439 // of sequence is treated for a HOLE and quits the sequence.
440 // Actually, we must unbind the vertices belonging to the
441 // loop from the map, but since they can't appear twice on the
442 // valid wire, leave them for a little speed up.
444 TColgp_SequenceOfPnt2d SeqPnt2d;
445 TColStd_DataMapOfIntegerInteger NodeInSeq;
446 // Start traversing the wire
447 for (WireExplorer.Init(TopoDS::Wire(FaceExplorer.Value()),Face); WireExplorer.More(); WireExplorer.Next())
449 edge = WireExplorer.Current();
451 if (debclass) { debedge++; cout << endl; cout << "#edge no "<<debedge <<endl;}
453 anOr = edge.Orientation();
454 if (anOr != TopAbs_FORWARD && anOr != TopAbs_REVERSED) continue;
455 if (edges.IsBound(edge))
458 // Define the direction for adding points to SeqPnt2d
459 Standard_Integer iFirst,iLast,iIncr;
460 const BRepMesh_PairOfPolygon& pair = edges.Find(edge);
461 Handle(Poly_PolygonOnTriangulation) NOD;
462 if (anOr == TopAbs_FORWARD)
466 iLast = NOD->NbNodes();
472 iFirst = NOD->NbNodes();
476 const TColStd_Array1OfInteger& indices = NOD->Nodes();
478 // indexFirst and indexLast are the indices of first and last
479 // vertices of the edge in IndexedMap <Str>
480 const Standard_Integer indexFirst = themap.FindKey(indices(iFirst));
481 const Standard_Integer indexLast = themap.FindKey(indices(iLast));
483 // Skip degenerated edge : OCC481(apo)
484 if (indexLast == indexFirst && (iLast-iFirst) == iIncr) continue;
486 // If there's a gap between edges -> raise <falsewire> flag
489 if (indexFirst != lastindex)
491 falsewire = Standard_True;
495 else firstindex = indexFirst;
496 lastindex = indexLast;
498 // Record first vertex (to detect loops)
499 NodeInSeq.Bind(indexFirst,SeqPnt2d.Length()+1);
501 // Add vertices in sequence
502 for (i = iFirst; i != iLast; i += iIncr)
504 index = (i == iFirst)? indexFirst : themap.FindKey(indices(i));
506 gp_Pnt2d vp(Str->GetNode(index).Coord());
509 if (debclass) cout<<"point p"<<index<<" "<<vp.X()<<" "<< vp.Y()<<endl;
513 // Now, is there a loop?
514 if (NodeInSeq.IsBound(indexLast))
516 // Yes, treat it separately as a hole
517 // 1. Divide points into main wire and a loop
518 const Standard_Integer iWireStart = NodeInSeq(indexLast);
519 if(iWireStart < SeqPnt2d.Length()) {
520 SeqPnt2d.Split(iWireStart, aWire);
521 //OCC319-> the operation will be done later
522 // 2. Proceed the loop
523 //AnalizeWire(aLoop, Umin, Umax, Vmin, Vmax, aWirePoints, aWireLength, NbBiPoint);
524 aWireLength.Append(aWire.Length());
525 aWirePoints.Append(aWire);
536 if (falsewire || (firstindex != lastindex) || SeqPnt2d.Length() > 1)
538 myState = BRepMesh_OpenWire;
545 cout <<"Warning : empty wire" <<endl;
550 const Standard_Integer nbwires = aWireLength.Length();
551 Standard_Integer NbBiPoint = aWirePoints.Length();
552 BRepMesh_Array1OfBiPoint BiPoints(0,NbBiPoint);
554 BRepMesh_BiPoint *BP;
555 Standard_Real *Coordinates1;
556 Standard_Real x1, y1, x2, y2, xstart, ystart;
557 Standard_Integer j, l = 1;
558 BP = &(BiPoints.ChangeValue(1));
560 // Fill array of segments (bi-points)
561 for (i = 1; i <= nbwires; i++)
563 const Standard_Integer len = aWireLength(i) + 1;
564 for (j = 1; j <= len; j++)
566 // Obtain last point of the segment
574 const gp_Pnt2d& PT = aWirePoints(l); l++;
578 // Build segment (bi-point)
586 Coordinates1 = ((Standard_Real*)(BP->Coordinates())); BP++;
587 Coordinates1[0] = x1;
588 Coordinates1[1] = y1;
589 Coordinates1[2] = x2;
590 Coordinates1[3] = y2;
591 Coordinates1[4] = x2 - x1;
592 Coordinates1[5] = y2 - y1;
599 // Search the intersection
600 // Explore first wire
601 Standard_Integer ik, ikEnd = 0;
602 for(i = 1; i <= nbwires; i++)
604 ik = ikEnd + 1; ikEnd += aWireLength(i);
605 // Explore second wire
606 for (j = i; j <= nbwires; j++)
608 if ( checkWiresIntersection(i, j, &ik, ikEnd, aWireLength, BiPoints) )
610 myState = BRepMesh_SelfIntersectingWire; return;
616 for (i = nbwires; i >= 1; i--)
618 NbBiPoint = aWirePoints.Length() - aWireLength(i) + 1;
619 aWirePoints.Split(NbBiPoint, aWire);
620 AnalizeWire(aWire, Umin, Umax, Vmin, Vmax);
625 //=======================================================================
628 //=======================================================================
630 TopAbs_State BRepMesh_Classifier::Perform(const gp_Pnt2d& aPoint) const
632 Standard_Boolean isOut = Standard_False;
634 Standard_Integer cur, i, nb = TabClass.Length();
636 for (i = 1; i <= nb; i++)
638 cur = ((CSLib_Class2d*)TabClass(i))->SiDans(aPoint);
641 // Point is ON, but mark it as OUT
642 isOut = Standard_True;
646 isOut = TabOrien(i)? (cur == -1) : (cur == 1);
648 if (isOut) return TopAbs_OUT;
655 //=======================================================================
658 //=======================================================================
660 void BRepMesh_Classifier::Destroy()
662 Standard_Integer i, nb = TabClass.Length();
663 for (i = 1; i <= nb; i++)
667 delete ((CSLib_Class2d*)TabClass(i));