1 // Created on: 2009-10-22
2 // Created by: Mikhail SAZONOV
3 // Copyright (c) 2009-2012 OPEN CASCADE SAS
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
21 #include <Poly_MakeLoops.hxx>
22 #include <NCollection_IncAllocator.hxx>
23 #include <NCollection_DataMap.hxx>
25 #include <gp_Dir2d.hxx>
28 static Standard_Integer doDebug = 0;
31 //=======================================================================
32 //function : Poly_MakeLoops
34 //=======================================================================
36 Poly_MakeLoops::Poly_MakeLoops(const Helper* theHelper,
37 const Handle_NCollection_BaseAllocator& theAlloc)
38 : myHelper (theHelper),
40 myMapLink (4000, myAlloc),
46 //=======================================================================
49 //=======================================================================
51 void Poly_MakeLoops::Reset
52 (const Helper* theHelper,
53 const Handle_NCollection_BaseAllocator& theAlloc)
57 myLoops.Clear(theAlloc);
58 myStartIndices.Clear();
62 //=======================================================================
65 //=======================================================================
67 void Poly_MakeLoops::AddLink(const Link& theLink)
69 if (theLink.node1 == theLink.node2)
71 Standard_Integer aInd = myMapLink.Add(theLink);
72 Link& aLink = const_cast<Link&>(myMapLink(aInd));
73 aLink.flags |= theLink.flags;
75 myHelper->OnAddLink (aInd, aLink);
79 //=======================================================================
80 //function : ReplaceLink
82 //=======================================================================
84 void Poly_MakeLoops::ReplaceLink(const Link& theLink, const Link& theNewLink)
86 if (theNewLink.node1 == theNewLink.node2)
88 Standard_Integer aInd = myMapLink.Add(theLink);
92 // replace with a null link first (workaround exception)
93 myMapLink.Substitute(aInd, aLink);
95 // and now put there the final value of link
96 myMapLink.Substitute(aInd, aLink);
98 myHelper->OnAddLink (aInd, aLink);
103 //=======================================================================
104 //function : SetLinkOrientation
106 //=======================================================================
108 Poly_MakeLoops::LinkFlag Poly_MakeLoops::SetLinkOrientation
109 (const Link& theLink,
110 const LinkFlag theOrient)
112 Standard_Integer aInd = myMapLink.FindIndex(theLink);
113 LinkFlag aOri = LF_None;
116 Link& aLink = const_cast<Link&>(myMapLink(aInd));
117 aOri = (LinkFlag) (aLink.flags & LF_Both);
118 aLink.flags = theOrient;
120 myHelper->OnAddLink (aInd, aLink);
126 //=======================================================================
127 //function : FindLink
129 //=======================================================================
131 Poly_MakeLoops::Link Poly_MakeLoops::FindLink(const Link& theLink) const
133 Standard_Integer aInd = myMapLink.FindIndex(theLink);
134 Poly_MakeLoops::Link aLink;
136 aLink = myMapLink(aInd);
140 //=======================================================================
143 //=======================================================================
145 Standard_Integer Poly_MakeLoops::Perform()
147 // prepare the set of start indices
148 myStartIndices.Clear();
150 for (i = 1; i <= myMapLink.Extent(); i++)
152 const Link& aLink = myMapLink(i);
153 if (aLink.flags & LF_Fwd)
154 myStartIndices.Add(i);
155 if (aLink.flags & LF_Rev)
156 myStartIndices.Add(-i);
161 showBoundaryBreaks();
164 Standard_Integer aResult = 0;
166 Handle(NCollection_IncAllocator) aTempAlloc = new NCollection_IncAllocator(4000);
167 Handle(NCollection_IncAllocator) aTempAlloc1 = new NCollection_IncAllocator(4000);
170 Standard_Integer aPassNum, nbLoopsOnPass2 = 0;
171 for (aPassNum=0; aPassNum < 2; aPassNum++)
173 myHangIndices.Clear();
175 while (!myStartIndices.IsEmpty())
177 Standard_Integer aIndexS = myStartIndices.Top();
180 NCollection_IndexedMap<Standard_Integer> aContour (100, aTempAlloc);
181 Standard_Integer aStartNumber = findContour (aIndexS, aContour, aTempAlloc, aTempAlloc1);
183 if (aStartNumber > 1)
186 cout << "--- found contour with hanging links:" << endl;
187 for (i = 1; i <= aContour.Extent(); i++)
188 cout << " " << aContour(i);
192 if (aStartNumber == 0)
194 aResult |= RC_Failure;
197 if (aStartNumber <= aContour.Extent())
199 // there is a closed loop in the contour
202 acceptContour (aContour, aStartNumber);
204 if (aStartNumber > 1)
206 // it is required to mark hanging edges
207 Standard_Integer aNode;
208 if (aStartNumber <= aContour.Extent())
209 // mark hanging edges starting from the first one till a bifurcation
210 aNode = getFirstNode(aIndexS);
213 // open contour - mark from the end back till a bifurcation
214 aIndexS = aContour(aStartNumber - 1);
215 aNode = getLastNode(aIndexS);
217 markHangChain(aNode, aIndexS);
223 // move hanging links to start indices to make the second pass
224 TColStd_MapIteratorOfPackedMapOfInteger it(myHangIndices);
225 for (; it.More(); it.Next())
226 myStartIndices.Add(it.Key());
230 if (doDebug && nbLoopsOnPass2)
231 cout << "MakeLoops: " << nbLoopsOnPass2
232 << " contours accepted on the second pass" << endl;
235 if (!myLoops.IsEmpty())
236 aResult |= RC_LoopsDone;
237 if (!myHangIndices.IsEmpty())
238 aResult |= RC_HangingLinks;
242 //=======================================================================
243 //function : findContour
244 //purpose : Collects edges in chain until they form a closed contour.
245 // Returns the index in the map theContour where the loop starts.
246 // It may return the number greater than the extent of the map by 1,
247 // what means that the contour is open
248 //=======================================================================
250 Standard_Integer Poly_MakeLoops::findContour
251 (Standard_Integer theIndexS,
252 NCollection_IndexedMap<Standard_Integer> &theContour,
253 const Handle_NCollection_BaseAllocator& theTempAlloc,
254 const Handle_NCollection_IncAllocator& theTempAlloc1) const
257 Standard_Integer aStartIndex = 0;
258 Standard_Integer aIndexS = theIndexS;
259 NCollection_DataMap<Standard_Integer,Standard_Integer> aNodeLink(100, theTempAlloc);
260 Standard_Integer aLastNode = getLastNode (aIndexS);
263 theContour.Add(aIndexS);
264 aNodeLink.Bind(getFirstNode(aIndexS), aIndexS);
266 Standard_Integer aIndex = Abs (aIndexS);
268 // collect the list of links from this node able to participate
270 theTempAlloc1->Reset();
271 NCollection_List<Standard_Integer> aLstIndS (theTempAlloc1);
272 const ListOfLink& aLinks = myHelper->GetAdjacentLinks (aLastNode);
273 Poly_MakeLoops::ListOfLink::Iterator itLinks (aLinks);
274 for (; itLinks.More(); itLinks.Next()) {
275 Standard_Integer aInd = myMapLink.FindIndex(itLinks.Value());
276 if (aInd == 0 || aInd == aIndex)
278 // determine the orientation in which the link is to be taken
279 Standard_Integer aIndS = aInd;
280 Standard_Integer aNode1 = getFirstNode(aInd);
281 if (aNode1 != aLastNode)
284 if (canLinkBeTaken(aIndS))
285 aLstIndS.Append(aIndS);
288 if (aLstIndS.IsEmpty()) {
289 // no more ways: open contour
290 aStartIndex = theContour.Extent() + 1;
294 Standard_Integer aIndexSNext = 0;
295 if (aLstIndS.First() == aLstIndS.Last())
296 // only one possible way
297 aIndexSNext = aLstIndS.First();
299 // find the most left way
300 aIndexSNext = chooseLeftWay (aLastNode, aIndexS, aLstIndS);
302 aIndexS = aIndexSNext;
306 // no more ways: open contour
307 aStartIndex = theContour.Extent() + 1;
310 if (theContour.Contains(aIndexS))
312 // entering the loop second time, stop search
313 aStartIndex = theContour.FindIndex (aIndexS);
316 if (theContour.Contains (-aIndexS))
318 // leaving the loop, stop search
319 aStartIndex = theContour.FindIndex (-aIndexS) + 1;
323 aLastNode = getLastNode (aIndexS);
325 if (aNodeLink.IsBound(aLastNode))
327 // closing the loop, stop search
328 theContour.Add(aIndexS);
329 aStartIndex = theContour.FindIndex(aNodeLink.Find(aLastNode));
337 //=======================================================================
338 //function : acceptContour
339 //purpose : Builds a wire from a given set of edge indices (starting with
340 // theStartNumber) and appends it to the result list.
341 // Also updates the start indices.
342 //=======================================================================
344 void Poly_MakeLoops::acceptContour
345 (const NCollection_IndexedMap<Standard_Integer>& theContour,
346 Standard_Integer theStartNumber)
348 // append a new loop to the result
349 Loop anEmptyLoop(myAlloc);
350 myLoops.Append(anEmptyLoop);
351 Loop& aLoop = myLoops.ChangeValue(myLoops.Length());
353 // build a loop, mark links as taken,
354 // remove them from the set of start indices
356 for (i = theStartNumber; i <= theContour.Extent(); i++)
358 Standard_Integer aIndexS = theContour(i); // index with sign
359 Standard_Integer aIndex = Abs (aIndexS);
360 const Link& aLink = myMapLink(aIndex);
361 Link aOrientedLink = aLink;
363 aOrientedLink.Reverse();
364 aLoop.Append(aOrientedLink);
365 // remove from start set
366 myStartIndices.Remove(aIndexS);
370 //=======================================================================
371 //function : getFirstNode
372 //purpose : Returns the first node of the given link
373 // taking into account its orientation (the sign of index)
374 //=======================================================================
376 Standard_Integer Poly_MakeLoops::getFirstNode(Standard_Integer theIndexS) const
378 Standard_Integer aIndex = Abs(theIndexS);
379 const Link& aLink = myMapLink(aIndex);
385 //=======================================================================
386 //function : getLastNode
387 //purpose : Returns the last node of the given link
388 // taking into account its orientation (the sign of index)
389 //=======================================================================
391 Standard_Integer Poly_MakeLoops::getLastNode(int theIndexS) const
393 Standard_Integer aIndex = Abs(theIndexS);
394 const Link& aLink = myMapLink(aIndex);
400 //=======================================================================
401 //function : markHangChain
402 //purpose : Marks hanging links starting from the given node.
403 // Also removes such links from the start indices.
404 //=======================================================================
406 void Poly_MakeLoops::markHangChain(Standard_Integer theNode, Standard_Integer theIndexS)
408 Standard_Integer aNode1 = theNode;
409 Standard_Integer aIndexS = theIndexS;
410 Standard_Integer aIndex = Abs(aIndexS);
411 Standard_Boolean isOut = (aNode1 == getFirstNode(aIndexS));
414 // check if the current link is hanging:
415 // if it is outcoming from aNode1 then count the number of
416 // other incoming links and vise versa;
417 // if the number is zero than it is hanging
418 const ListOfLink& aLinks = myHelper->GetAdjacentLinks (aNode1);
419 Standard_Integer nEdges = 0;
420 Poly_MakeLoops::ListOfLink::Iterator itLinks (aLinks);
421 for (; itLinks.More() && nEdges == 0; itLinks.Next())
423 const Link &aL = itLinks.Value();
424 Standard_Integer aInd = myMapLink.FindIndex(aL);
425 if (aInd == 0 || aInd == aIndex)
427 if (isOut && aNode1 == aL.node1 ||
428 !isOut && aNode1 == aL.node2)
430 if (canLinkBeTaken(aInd))
437 // mark the current link as hanging
438 myStartIndices.Remove(aIndexS);
439 myHangIndices.Add(aIndexS);
441 // get other node of the link and the next link
443 aNode1 = getLastNode(aIndexS);
445 aNode1 = getFirstNode(aIndexS);
446 const ListOfLink& aNextLinks = myHelper->GetAdjacentLinks (aNode1);
447 Standard_Integer aNextIndexS = 0;
448 for (itLinks.Init(aNextLinks); itLinks.More(); itLinks.Next())
450 const Link &aL = itLinks.Value();
451 Standard_Integer aInd = myMapLink.FindIndex(aL);
452 if (aInd == 0 || aInd == aIndex)
454 if (isOut && aNode1 == aL.node2 ||
455 !isOut && aNode1 == aL.node1)
457 if (canLinkBeTaken(aInd))
459 if (aNextIndexS == 0)
463 // more than 1 ways, stop the chain
469 if (aNextIndexS == 0)
471 aIndexS = aNextIndexS;
472 aIndex = Abs(aIndexS);
476 //=======================================================================
477 //function : canLinkBeTaken
478 //purpose : Returns True if the link appointed by the index can participate
479 // in a loop in given orientation (it is the sign of index).
480 // Remark: A boundary edge can be taken only once
481 //=======================================================================
483 Standard_Boolean Poly_MakeLoops::canLinkBeTaken(Standard_Integer theIndexS) const
485 return myStartIndices.Contains(theIndexS);
488 //=======================================================================
489 //function : showBoundaryBreaks
491 //=======================================================================
494 void Poly_MakeLoops::showBoundaryBreaks() const
496 // collect nodes of boundary links
497 TColStd_PackedMapOfInteger aNodesMap;
499 for (i = 1; i <= myMapLink.Extent(); i++)
501 const Link& aLink = myMapLink(i);
502 Standard_Integer aFlags = aLink.flags & LF_Both;
503 if (aFlags && aFlags != LF_Both)
505 // take only oriented links
506 aNodesMap.Add(aLink.node1);
507 aNodesMap.Add(aLink.node2);
511 // check each node if the number of input and output links are equal
512 Standard_Boolean isFirst = Standard_True;
513 TColStd_MapIteratorOfPackedMapOfInteger it(aNodesMap);
514 for (; it.More(); it.Next())
516 Standard_Integer aNode = it.Key();
517 Standard_Integer nb = 0;
518 const ListOfLink& aLinks = myHelper->GetAdjacentLinks(aNode);
519 Poly_MakeLoops::ListOfLink::Iterator itLinks (aLinks);
520 for (; itLinks.More(); itLinks.Next())
522 const Poly_MakeLoops::Link& aLink = itLinks.Value();
523 if (myMapLink.FindIndex(aLink) == 0)
525 Standard_Integer aFlags = aLink.flags & LF_Both;
526 if (aFlags && aFlags != LF_Both)
528 if (aNode == aLink.node1) // output?
533 nb--; // reversed, so input
535 else if (aNode == aLink.node2) // input?
540 nb++; // reversed, so output
549 // indicate this node
552 isFirst = Standard_False;
553 cout << "boundary breaks are found in the following nodes:" << endl;
555 cout << aNode << " ";
563 //=======================================================================
564 //function : GetHangingLinks
566 //=======================================================================
568 void Poly_MakeLoops::GetHangingLinks(ListOfLink& theLinks) const
570 TColStd_MapIteratorOfPackedMapOfInteger it(myHangIndices);
571 for (; it.More(); it.Next())
573 Standard_Integer aIndexS = it.Key();
574 Link aLink = myMapLink(Abs(aIndexS));
577 theLinks.Append(aLink);
581 //=======================================================================
582 //function : Poly_MakeLoops3D
584 //=======================================================================
586 Poly_MakeLoops3D::Poly_MakeLoops3D(const Helper* theHelper,
587 const Handle_NCollection_BaseAllocator& theAlloc)
588 : Poly_MakeLoops (theHelper, theAlloc)
592 //=======================================================================
593 //function : Poly_MakeLoops3D::chooseLeftWay
595 //=======================================================================
597 Standard_Integer Poly_MakeLoops3D::chooseLeftWay
598 (const Standard_Integer theNode,
599 const Standard_Integer theSegIndex,
600 const NCollection_List<Standard_Integer>& theLstIndS) const
602 Standard_Real aAngleMin = M_PI * 2;
604 const Helper* aHelper = getHelper();
605 if (!aHelper->GetNormal (theNode, aNormal))
606 return theLstIndS.First();
608 Link aLink = getLink(theSegIndex);
610 if (!aHelper->GetLastTangent (aLink, aTgtRef))
611 return theLstIndS.First();
613 // project tangent vector to the plane orthogonal to normal
614 // to get the reference direction
615 gp_XYZ aTgtRefXYZ = aNormal.XYZ().CrossCrossed (aTgtRef.XYZ(), aNormal.XYZ());
616 if (aTgtRefXYZ.SquareModulus() < 1e-14)
617 // a problem with defining reference direction, take first way
618 return theLstIndS.First();
619 aTgtRef = aTgtRefXYZ;
621 // find the way with minimal angle to the reference direction
622 // (the angle is in range ]-PI;PI])
623 Standard_Integer aResIndex = 0;
624 NCollection_List<Standard_Integer>::Iterator aItI (theLstIndS);
625 for (; aItI.More(); aItI.Next())
627 Standard_Integer aIndS = aItI.Value();
629 aLink = getLink(aIndS);
631 if (!aHelper->GetFirstTangent (aLink, aTgt))
634 gp_XYZ aTgtXYZ = aNormal.XYZ().CrossCrossed (aTgt.XYZ(), aNormal.XYZ());
635 if (aTgtXYZ.SquareModulus() < 1e-14)
636 // skip a problem way
640 Standard_Real aAngle = aTgt.AngleWithRef(aTgtRef, aNormal);
641 if (aAngle < 1e-4 - M_PI)
643 if (aAngle < aAngleMin)
649 return aResIndex == 0 ? theLstIndS.First() : aResIndex;
652 //=======================================================================
653 //function : Poly_MakeLoops2D
655 //=======================================================================
657 Poly_MakeLoops2D::Poly_MakeLoops2D(const Standard_Boolean theLeftWay,
658 const Helper* theHelper,
659 const Handle_NCollection_BaseAllocator& theAlloc)
660 : Poly_MakeLoops (theHelper, theAlloc),
661 myRightWay(!theLeftWay)
665 //=======================================================================
666 //function : Poly_MakeLoops2D::chooseLeftWay
668 //=======================================================================
670 Standard_Integer Poly_MakeLoops2D::chooseLeftWay
671 (const Standard_Integer /*theNode*/,
672 const Standard_Integer theSegIndex,
673 const NCollection_List<Standard_Integer>& theLstIndS) const
675 Standard_Real aAngleMin = M_PI * 2;
676 const Helper* aHelper = getHelper();
677 Link aLink = getLink(theSegIndex);
679 if (!aHelper->GetLastTangent (aLink, aTgtRef))
680 // a problem with defining reference direction, take first way
681 return theLstIndS.First();
683 // find the way with minimal angle to the reference direction
684 // (the angle is in range ]-PI;PI])
685 Standard_Integer aResIndex = 0;
686 NCollection_List<Standard_Integer>::Iterator aItI (theLstIndS);
687 for (; aItI.More(); aItI.Next())
689 Standard_Integer aIndS = aItI.Value();
691 aLink = getLink(aIndS);
693 if (!aHelper->GetFirstTangent (aLink, aTgt))
694 // skip a problem way
697 Standard_Real aAngle = aTgt.Angle(aTgtRef);
700 if (aAngle < 1e-4 - M_PI)
702 if (aAngle < aAngleMin)
708 return aResIndex == 0 ? theLstIndS.First() : aResIndex;