// Created on: 2009-10-22
// Created by: Mikhail SAZONOV
// Copyright (c) 2009-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef Poly_MakeLoops_HeaderFile
#define Poly_MakeLoops_HeaderFile
#include "NCollection_Sequence.hxx"
#include "NCollection_IndexedMap.hxx"
#include "TColStd_PackedMapOfInteger.hxx"
#include "NCollection_IncAllocator.hxx"
#include "NCollection_List.hxx"
#include "NCollection_OccAllocator.hxx"
#include "Standard_HashUtils.hxx"
/**
* Make loops from a set of connected links. A link is represented by
* a pair of integer indices of nodes.
*/
class Poly_MakeLoops
{
public:
//! Orientation flags that can be attached to a link
enum LinkFlag
{
LF_None = 0,
LF_Fwd = 1, // forward orientation
LF_Rev = 2, // reversed orientation
LF_Both = 3, // both ways oriented
LF_Reversed = 4 // means the link is reversed
};
//! The Link structure
struct Link
{
int node1, node2;
int flags;
Link()
: node1(0),
node2(0),
flags(0)
{
}
Link(int theNode1, int theNode2)
: node1(theNode1),
node2(theNode2),
flags(1)
{
}
void Reverse() { flags ^= Poly_MakeLoops::LF_Reversed; }
bool IsReversed() const { return (flags & Poly_MakeLoops::LF_Reversed) != 0; }
void Nullify() { node1 = node2 = 0; }
bool IsNull() const { return node1 == 0 || node2 == 0; }
bool operator==(const Link& theOther) const
{
return (theOther.node1 == node1 && theOther.node2 == node2)
|| (theOther.node1 == node2 && theOther.node2 == node1);
}
};
struct Hasher
{
size_t operator()(const Poly_MakeLoops::Link& theLink) const noexcept
{
// Combine two int values into a single hash value.
int aCombination[2]{theLink.node1, theLink.node2};
if (aCombination[0] > aCombination[1])
{
std::swap(aCombination[0], aCombination[1]);
}
return opencascade::hashBytes(aCombination, sizeof(aCombination));
}
bool operator()(const Poly_MakeLoops::Link& theLink1,
const Poly_MakeLoops::Link& theLink2) const noexcept
{
return theLink1 == theLink2;
}
};
// Define the Loop as a list of links
typedef NCollection_List ListOfLink;
typedef ListOfLink Loop;
//! The abstract helper class
class Helper
{
public:
//! returns the links adjacent to the given node
virtual const ListOfLink& GetAdjacentLinks(int theNode) const = 0;
//! hook function called from AddLink in _DEBUG mode
virtual void OnAddLink(int /*theNum*/, const Link& /*theLink*/) const {}
};
//! This class implements a heap of integers. The most effective usage
//! of it is first to add there all items, and then get top item and remove
//! any items till it becomes empty.
class HeapOfInteger
{
public:
HeapOfInteger(const int theNbPreAllocated = 1)
: myMap(theNbPreAllocated),
myIterReady(false)
{
}
void Clear()
{
myMap.Clear();
myIterReady = false;
}
void Add(const int theValue)
{
myMap.Add(theValue);
myIterReady = false;
}
int Top()
{
if (!myIterReady)
{
myIter.Initialize(myMap);
myIterReady = true;
}
return myIter.Key();
}
bool Contains(const int theValue) const { return myMap.Contains(theValue); }
void Remove(const int theValue)
{
if (myIterReady && myIter.More() && myIter.Key() == theValue)
myIter.Next();
myMap.Remove(theValue);
}
bool IsEmpty()
{
if (!myIterReady)
{
myIter.Initialize(myMap);
myIterReady = true;
}
return !myIter.More();
}
private:
TColStd_PackedMapOfInteger myMap;
TColStd_PackedMapOfInteger::Iterator myIter;
bool myIterReady;
};
public:
// PUBLIC METHODS
//! Constructor. If helper is NULL then the algorithm will
//! probably return a wrong result
Standard_EXPORT Poly_MakeLoops(const Helper* theHelper,
const occ::handle& theAlloc = nullptr);
//! It is to reset the algorithm to the initial state.
Standard_EXPORT void Reset(const Helper* theHelper,
const occ::handle& theAlloc = nullptr);
//! Adds a link to the set. theOrient defines which orientations of the link
//! are allowed.
Standard_EXPORT void AddLink(const Link& theLink);
//! Replace one link with another (e.g. to change order of nodes)
Standard_EXPORT void ReplaceLink(const Link& theLink, const Link& theNewLink);
//! Set a new value of orientation of a link already added earlier.
//! It can be used with LF_None to exclude the link from consideration.
//! Returns the old value of orientation.
Standard_EXPORT LinkFlag SetLinkOrientation(const Link& theLink, const LinkFlag theOrient);
//! Find the link stored in algo by value
Standard_EXPORT Link FindLink(const Link& theLink) const;
enum ResultCode
{
RC_LoopsDone = 1,
RC_HangingLinks = 2,
RC_Failure = 4
};
//! Does the work. Returns the collection of result codes
Standard_EXPORT int Perform();
//! Returns the number of loops in the result
int GetNbLoops() const { return myLoops.Length(); }
//! Returns the loop of the given index
const Loop& GetLoop(int theIndex) const { return myLoops.Value(theIndex); }
//! Returns the number of detected hanging chains
int GetNbHanging() const { return myHangIndices.Extent(); }
//! Fills in the list of hanging links
Standard_EXPORT void GetHangingLinks(ListOfLink& theLinks) const;
protected:
virtual int chooseLeftWay(const int theNode,
const int theSegIndex,
const NCollection_List& theLstIndS) const = 0;
const Helper* getHelper() const { return myHelper; }
Link getLink(const int theSegIndex) const
{
Link aLink = myMapLink(std::abs(theSegIndex));
if (theSegIndex < 0)
aLink.Reverse();
return aLink;
}
#ifdef OCCT_DEBUG
void showBoundaryBreaks() const;
#endif
private:
int findContour(int theIndexS,
NCollection_IndexedMap& theContour,
const occ::handle& theTempAlloc,
const occ::handle& theTempAlloc1) const;
void acceptContour(const NCollection_IndexedMap& theContour, int theStartNumber);
int getFirstNode(int theIndexS) const;
int getLastNode(int theIndexS) const;
void markHangChain(int theNode, int theIndexS);
bool canLinkBeTaken(int theIndexS) const;
// FIELDS
const Helper* myHelper;
occ::handle myAlloc;
NCollection_IndexedMap myMapLink;
NCollection_Sequence myLoops;
HeapOfInteger myStartIndices;
TColStd_PackedMapOfInteger myHangIndices;
};
/**
* Implementation for 3D space
*/
class gp_Dir;
class Poly_MakeLoops3D : public Poly_MakeLoops
{
public:
//! The abstract helper class
class Helper : public Poly_MakeLoops::Helper
{
public:
// all the following methods should return False if
// it is impossible to return a valid direction
//! returns the tangent vector at the first node of a link
virtual bool GetFirstTangent(const Link& theLink, gp_Dir& theDir) const = 0;
//! returns the tangent vector at the last node of a link
virtual bool GetLastTangent(const Link& theLink, gp_Dir& theDir) const = 0;
//! returns the normal to the surface at a given node
virtual bool GetNormal(int theNode, gp_Dir& theDir) const = 0;
};
//! Constructor. If helper is NULL then the algorithm will
//! probably return a wrong result
Standard_EXPORT Poly_MakeLoops3D(const Helper* theHelper,
const occ::handle& theAlloc);
protected:
Standard_EXPORT int chooseLeftWay(const int theNode,
const int theSegIndex,
const NCollection_List& theLstIndS) const override;
const Helper* getHelper() const
{
return static_cast(Poly_MakeLoops::getHelper());
}
};
/**
* Implementation for 2D space
*/
class gp_Dir2d;
class Poly_MakeLoops2D : public Poly_MakeLoops
{
public:
//! The abstract helper class
class Helper : public Poly_MakeLoops::Helper
{
public:
// all the following methods should return False if
// it is impossible to return a valid direction
//! returns the tangent vector at the first node of a link
virtual bool GetFirstTangent(const Link& theLink, gp_Dir2d& theDir) const = 0;
//! returns the tangent vector at the last node of a link
virtual bool GetLastTangent(const Link& theLink, gp_Dir2d& theDir) const = 0;
};
//! Constructor. If helper is NULL then the algorithm will
//! probably return a wrong result
Standard_EXPORT Poly_MakeLoops2D(const bool theLeftWay,
const Helper* theHelper,
const occ::handle& theAlloc);
protected:
Standard_EXPORT int chooseLeftWay(const int theNode,
const int theSegIndex,
const NCollection_List& theLstIndS) const override;
const Helper* getHelper() const
{
return static_cast(Poly_MakeLoops::getHelper());
}
private:
//! this flag says that chooseLeftWay must choose the right way instead
bool myRightWay;
};
namespace std
{
template <>
struct hash
{
size_t operator()(const Poly_MakeLoops::Link& theLink) const noexcept
{
return Poly_MakeLoops::Hasher{}(theLink);
}
};
template <>
struct equal_to
{
bool operator()(const Poly_MakeLoops::Link& theLink1,
const Poly_MakeLoops::Link& theLink2) const noexcept
{
return theLink1 == theLink2;
}
};
} // namespace std
#endif