Part/Toponaming: Add original implementation of makEWires
Renamed to makeElementWires and modified to compile in the current codebase.
This commit is contained in:
@@ -48,7 +48,10 @@ class Color;
|
||||
namespace Part
|
||||
{
|
||||
|
||||
class ShapeHasher;
|
||||
class TopoShape;
|
||||
class TopoShapeCache;
|
||||
typedef std::unordered_map<TopoShape, TopoShape, ShapeHasher, ShapeHasher> TopoShapeMap;
|
||||
|
||||
/* A special sub-class to indicate null shapes
|
||||
*/
|
||||
@@ -610,6 +613,7 @@ public:
|
||||
bool canMapElement(const TopoShape &other) const;
|
||||
void mapSubElement(const TopoShape &other,const char *op=nullptr, bool forceHasher=false);
|
||||
void mapSubElement(const std::vector<TopoShape> &shapes, const char *op);
|
||||
void mapSubElementsTo(std::vector<TopoShape> &shapes, const char *op=nullptr) const;
|
||||
bool hasPendingElementMap() const;
|
||||
|
||||
|
||||
@@ -627,7 +631,117 @@ public:
|
||||
* a reference so that multiple operations can be carried out for
|
||||
* the same shape in the same line of code.
|
||||
*/
|
||||
TopoShape &makeElementCompound(const std::vector<TopoShape> &shapes, const char *op=nullptr, bool force=true);
|
||||
TopoShape& makeElementCompound(const std::vector<TopoShape>& shapes,
|
||||
const char* op = nullptr,
|
||||
bool force = true);
|
||||
|
||||
|
||||
/** Make a compound of wires by connecting input edges
|
||||
*
|
||||
* @param shapes: input shapes. Can be any type of shape. Edges will be
|
||||
* extracted for building wires.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param keepOrder: whether to respect the order of the input edges
|
||||
* @param tol: tolerance for checking the distance of two vertex to decide
|
||||
* if two edges are connected
|
||||
* @param shared: if true, then only connect edges if they shared the same
|
||||
* vertex, or else use \c tol to check for connection.
|
||||
* @param output: optional output mapping from wire edges to input edge.
|
||||
* Note that edges may be modified after adding to the wire,
|
||||
* so the output edges may not be the same as the input
|
||||
* ones.
|
||||
*
|
||||
* @return The function produces either a wire or a compound of wires. The
|
||||
* original content of this TopoShape is discarded and replaced
|
||||
* with the new shape. The function returns the TopoShape itself as
|
||||
* a reference so that multiple operations can be carried out for
|
||||
* the same shape in the same line of code.
|
||||
*/
|
||||
TopoShape& makeElementWires(const std::vector<TopoShape>& shapes,
|
||||
const char* op = nullptr,
|
||||
double tol = 0.0,
|
||||
bool shared = false,
|
||||
TopoShapeMap* output = nullptr);
|
||||
|
||||
/** Make a compound of wires by connecting input edges
|
||||
*
|
||||
* @param shape: input shape. Can be any type of shape. Edges will be
|
||||
* extracted for building wires.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param keepOrder: whether to respect the order of the input edges
|
||||
* @param tol: tolerance for checking the distance of two vertex to decide
|
||||
* if two edges are connected
|
||||
* @param shared: if true, then only connect edges if they shared the same
|
||||
* vertex, or else use \c tol to check for connection.
|
||||
* @param output: optional output mapping from wire edges to input edge.
|
||||
* Note that edges may be modified after adding to the wire,
|
||||
* so the output edges may not be the same as the input
|
||||
* ones.
|
||||
*
|
||||
* @return The function produces either a wire or a compound of wires. The
|
||||
* original content of this TopoShape is discarded and replaced
|
||||
* with the new shape. The function returns the TopoShape itself as
|
||||
* a reference so that multiple operations can be carried out for
|
||||
* the same shape in the same line of code.
|
||||
*/
|
||||
TopoShape& makeElementWires(const TopoShape& shape,
|
||||
const char* op = nullptr,
|
||||
double tol = 0.0,
|
||||
bool shared = false,
|
||||
TopoShapeMap* output = nullptr);
|
||||
|
||||
/** Make a compound of wires by connecting input edges in the given order
|
||||
*
|
||||
* @param shapes: input shapes. Can be any type of shape. Edges will be
|
||||
* extracted for building wires.
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param tol: tolerance for checking the distance of two vertex to decide
|
||||
* if two edges are connected
|
||||
* @param output: optional output mapping from wire edges to input edge.
|
||||
* Note that edges may be modified after adding to the wire,
|
||||
* so the output edges may not be the same as the input
|
||||
* ones.
|
||||
*
|
||||
* @return Same as makeElementWires() but respects the order of the input edges.
|
||||
* The function produces either a wire or a compound of wires. The
|
||||
* original content of this TopoShape is discarded and replaced
|
||||
* with the new shape. The function returns the TopoShape itself as
|
||||
* a reference so that multiple operations can be carried out for
|
||||
* the same shape in the same line of code.
|
||||
*/
|
||||
TopoShape& makeElementOrderedWires(const std::vector<TopoShape>& shapes,
|
||||
const char* op = nullptr,
|
||||
double tol = 0.0,
|
||||
TopoShapeMap* output = nullptr);
|
||||
|
||||
/** Make a wire or compound of wires with the edges inside the this shape
|
||||
*
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param keepOrder: whether to respect the order of the input edges
|
||||
* @param tol: tolerance for checking the distance of two vertex to decide
|
||||
* if two edges are connected
|
||||
* @param shared: if true, then only connect edges if they shared the same
|
||||
* vertex, or else use \c tol to check for connection.
|
||||
* @param output: optional output mapping from wire edges to input edge.
|
||||
* Note that edges may be modified after adding to the wire,
|
||||
* so the output edges may not be the same as the input
|
||||
* ones.
|
||||
*
|
||||
*
|
||||
* @return The function returns a new shape of either a single wire or a
|
||||
* compound of wires. The shape itself is not modified.
|
||||
*/
|
||||
TopoShape makeElementWires(const char* op = nullptr,
|
||||
double tol = 0.0,
|
||||
bool shared = false,
|
||||
TopoShapeMap* output = nullptr) const
|
||||
{
|
||||
return TopoShape(0, Hasher).makeElementWires(*this, op, tol, shared, output);
|
||||
}
|
||||
|
||||
friend class TopoShapeCache;
|
||||
|
||||
@@ -761,6 +875,19 @@ private:
|
||||
bool& warned);
|
||||
void mapCompoundSubElements(const std::vector<TopoShape>& shapes, const char* op);
|
||||
|
||||
/** Given a set of edges, return a sorted list of connected edges
|
||||
*
|
||||
* @param edges: (input/output) input list of shapes. Must be of type edge.
|
||||
* On return, the returned connected edges will be removed
|
||||
* from this list. You can repeated call this function to find
|
||||
* all wires.
|
||||
* @param keepOrder: whether to respect the order of the input edges
|
||||
* @param tol: tolerance for checking the distance of two vertex to decide
|
||||
* if two edges are connected
|
||||
* @return Return a list of ordered connected edges.
|
||||
*/
|
||||
static std::deque<TopoShape>
|
||||
sortEdges(std::list<TopoShape>& edges, bool keepOrder = false, double tol = 0.0);
|
||||
};
|
||||
|
||||
} // namespace Part
|
||||
|
||||
@@ -27,10 +27,16 @@
|
||||
#ifndef _PreComp_
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <TopTools_HSequenceOfShape.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <ShapeAnalysis_FreeBounds.hxx>
|
||||
#endif
|
||||
|
||||
#include "TopoShape.h"
|
||||
#include "TopoShapeCache.h"
|
||||
#include "TopoShapeOpCode.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
|
||||
|
||||
@@ -429,6 +435,11 @@ void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool force
|
||||
mapSubElementForShape(other, op);
|
||||
}
|
||||
|
||||
void TopoShape::mapSubElementsTo(std::vector<TopoShape> &shapes, const char *op) const {
|
||||
for(auto &shape : shapes)
|
||||
shape.mapSubElement(*this,op);
|
||||
}
|
||||
|
||||
std::vector<Data::ElementMap::MappedChildElements>
|
||||
TopoShape::createChildMap(size_t count, const std::vector<TopoShape>& shapes, const char* op)
|
||||
{
|
||||
@@ -540,4 +551,273 @@ TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes, const char*
|
||||
return *this;
|
||||
}
|
||||
|
||||
TopoShape &TopoShape::makeElementWires(const std::vector<TopoShape> &shapes,
|
||||
const char *op,
|
||||
double tol,
|
||||
bool shared,
|
||||
TopoShapeMap *output)
|
||||
{
|
||||
if(shapes.empty())
|
||||
FC_THROWM(NullShapeException, "Null shape");;
|
||||
if(shapes.size() == 1)
|
||||
return makeElementWires(shapes[0],op,tol,shared,output);
|
||||
return makeElementWires(TopoShape(Tag).makeElementCompound(shapes),op,tol,shared,output);
|
||||
}
|
||||
|
||||
|
||||
TopoShape &TopoShape::makeElementWires(const TopoShape &shape,
|
||||
const char *op,
|
||||
double tol,
|
||||
bool shared,
|
||||
TopoShapeMap *output)
|
||||
{
|
||||
if(!op) op = Part::OpCodes::Wire;
|
||||
if(tol<Precision::Confusion()) tol = Precision::Confusion();
|
||||
|
||||
if (shared) {
|
||||
// Can't use ShapeAnalysis_FreeBounds if not shared. It seems the output
|
||||
// edges are modified some how, and it is not obvious how to map the
|
||||
// resulting edges.
|
||||
Handle(TopTools_HSequenceOfShape) hEdges = new TopTools_HSequenceOfShape();
|
||||
Handle(TopTools_HSequenceOfShape) hWires = new TopTools_HSequenceOfShape();
|
||||
for(TopExp_Explorer xp(shape.getShape(),TopAbs_EDGE);xp.More();xp.Next())
|
||||
hEdges->Append(xp.Current());
|
||||
if(!hEdges->Length())
|
||||
FC_THROWM(NullShapeException, "Null shape");;
|
||||
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(hEdges, tol, Standard_True, hWires);
|
||||
if(!hWires->Length())
|
||||
FC_THROWM(NullShapeException, "Null shape");;
|
||||
|
||||
std::vector<TopoShape> wires;
|
||||
for (int i=1; i<=hWires->Length(); i++) {
|
||||
auto wire = hWires->Value(i);
|
||||
wires.push_back(TopoShape(Tag,Hasher,wire));
|
||||
}
|
||||
shape.mapSubElementsTo(wires,op);
|
||||
return makeElementCompound(wires,"",false);
|
||||
}
|
||||
|
||||
std::vector<TopoShape> wires;
|
||||
std::list<TopoShape> edge_list;
|
||||
|
||||
for(auto &e : shape.getSubTopoShapes(TopAbs_EDGE))
|
||||
edge_list.emplace_back(e);
|
||||
|
||||
std::vector<TopoShape> edges;
|
||||
edges.reserve(edge_list.size());
|
||||
wires.reserve(edge_list.size());
|
||||
|
||||
// sort them together to wires
|
||||
while (edge_list.size() > 0) {
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
// add and erase first edge
|
||||
edges.clear();
|
||||
edges.push_back(edge_list.front());
|
||||
mkWire.Add(TopoDS::Edge(edges.back().getShape()));
|
||||
edges.back().setShape(mkWire.Edge(),false);
|
||||
if (output)
|
||||
(*output)[edges.back()] = edge_list.front();
|
||||
edge_list.pop_front();
|
||||
|
||||
TopoDS_Wire new_wire = mkWire.Wire(); // current new wire
|
||||
|
||||
// try to connect each edge to the wire, the wire is complete if no more edges are connectible
|
||||
bool found = false;
|
||||
do {
|
||||
found = false;
|
||||
for (auto it=edge_list.begin();it!=edge_list.end();++it) {
|
||||
mkWire.Add(TopoDS::Edge(it->getShape()));
|
||||
if (mkWire.Error() != BRepBuilderAPI_DisconnectedWire) {
|
||||
// edge added ==> remove it from list
|
||||
found = true;
|
||||
edges.push_back(*it);
|
||||
// MakeWire will replace vertex of connected edge, which
|
||||
// effectively creat a new edge. So we need to update the
|
||||
// shape in order to preserve element mapping.
|
||||
edges.back().setShape(mkWire.Edge(),false);
|
||||
if (output)
|
||||
(*output)[edges.back()] = *it;
|
||||
edge_list.erase(it);
|
||||
new_wire = mkWire.Wire();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found);
|
||||
|
||||
wires.emplace_back(new_wire);
|
||||
wires.back().mapSubElement(edges, op);
|
||||
wires.back().fix();
|
||||
}
|
||||
return makeElementCompound(wires,0,false);
|
||||
}
|
||||
|
||||
|
||||
struct EdgePoints {
|
||||
gp_Pnt v1, v2;
|
||||
std::list<TopoShape>::iterator it;
|
||||
const TopoShape &edge;
|
||||
bool closed;
|
||||
|
||||
EdgePoints(std::list<TopoShape>::iterator it, double tol)
|
||||
:it(it), edge(*it), closed(false)
|
||||
{
|
||||
TopExp_Explorer xp(it->getShape(),TopAbs_VERTEX);
|
||||
v1 = BRep_Tool::Pnt(TopoDS::Vertex(xp.Current()));
|
||||
xp.Next();
|
||||
if (xp.More()) {
|
||||
v2 = BRep_Tool::Pnt(TopoDS::Vertex(xp.Current()));
|
||||
closed = (v2.SquareDistance(v1) <= tol);
|
||||
} else {
|
||||
v2 = v1;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::deque<TopoShape>
|
||||
TopoShape::sortEdges(std::list<TopoShape>& edges, bool keepOrder, double tol)
|
||||
{
|
||||
if (tol<Precision::Confusion()) tol = Precision::Confusion();
|
||||
double tol3d = tol * tol;
|
||||
|
||||
std::list<EdgePoints> edge_points;
|
||||
for (auto it = edges.begin(); it != edges.end(); ++it)
|
||||
edge_points.emplace_back(it, tol3d);
|
||||
|
||||
std::deque<TopoShape> sorted;
|
||||
if (edge_points.empty())
|
||||
return sorted;
|
||||
|
||||
gp_Pnt first, last;
|
||||
first = edge_points.front().v1;
|
||||
last = edge_points.front().v2;
|
||||
|
||||
sorted.push_back(edge_points.front().edge);
|
||||
edges.erase(edge_points.front().it);
|
||||
if (edge_points.front().closed)
|
||||
return sorted;
|
||||
|
||||
edge_points.erase(edge_points.begin());
|
||||
|
||||
auto reverseEdge = [](const TopoShape &edge) {
|
||||
Standard_Real first, last;
|
||||
const Handle(Geom_Curve) & curve = BRep_Tool::Curve(TopoDS::Edge(edge.getShape()), first, last);
|
||||
first = curve->ReversedParameter(first);
|
||||
last = curve->ReversedParameter(last);
|
||||
TopoShape res(BRepBuilderAPI_MakeEdge(curve->Reversed(), last, first));
|
||||
auto edgeName = Data::IndexedName::fromConst("Edge", 1);
|
||||
if (auto mapped = edge.getMappedName(edgeName))
|
||||
res.elementMap()->setElementName(edgeName, mapped, Tag);
|
||||
auto v1Name = Data::IndexedName::fromConst("Vertex", 1);
|
||||
auto v2Name = Data::IndexedName::fromConst("Vertex", 2);
|
||||
auto v1 = edge.getMappedName(v1Name);
|
||||
auto v2 = edge.getMappedName(v2Name);
|
||||
if (v1 && v2) {
|
||||
res.elementMap()->setElementName(v1Name, v2, Tag);
|
||||
res.elementMap()->setElementName(v2Name, v1, Tag);
|
||||
}
|
||||
else if (v1 && edge.countSubShapes(TopAbs_EDGE) == 1) {
|
||||
// It's possible an edge has only one vertex, so no need to reverse
|
||||
// the name
|
||||
res.elementMap()->setElementName(v1Name, v1, Tag);
|
||||
}
|
||||
else if (v1)
|
||||
res.elementMap()->setElementName(v2Name, v1, Tag);
|
||||
else if (v2)
|
||||
res.elementMap()->setElementName(v1Name, v2, Tag);
|
||||
return res;
|
||||
};
|
||||
|
||||
while (!edge_points.empty()) {
|
||||
// search for adjacent edge
|
||||
std::list<EdgePoints>::iterator pEI;
|
||||
for (pEI = edge_points.begin(); pEI != edge_points.end(); ++pEI) {
|
||||
if (pEI->closed)
|
||||
continue;
|
||||
|
||||
if (keepOrder && sorted.size() == 1) {
|
||||
if (pEI->v2.SquareDistance(first) <= tol3d
|
||||
|| pEI->v1.SquareDistance(first) <= tol3d) {
|
||||
sorted[0] = reverseEdge(sorted[0]);
|
||||
std::swap(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
if (pEI->v1.SquareDistance(last) <= tol3d) {
|
||||
last = pEI->v2;
|
||||
sorted.push_back(pEI->edge);
|
||||
edges.erase(pEI->it);
|
||||
edge_points.erase(pEI);
|
||||
pEI = edge_points.begin();
|
||||
break;
|
||||
}
|
||||
else if (pEI->v2.SquareDistance(first) <= tol3d) {
|
||||
sorted.push_front(pEI->edge);
|
||||
first = pEI->v1;
|
||||
edges.erase(pEI->it);
|
||||
edge_points.erase(pEI);
|
||||
pEI = edge_points.begin();
|
||||
break;
|
||||
}
|
||||
else if (pEI->v2.SquareDistance(last) <= tol3d) {
|
||||
last = pEI->v1;
|
||||
sorted.push_back(reverseEdge(pEI->edge));
|
||||
edges.erase(pEI->it);
|
||||
edge_points.erase(pEI);
|
||||
pEI = edge_points.begin();
|
||||
break;
|
||||
}
|
||||
else if (pEI->v1.SquareDistance(first) <= tol3d) {
|
||||
first = pEI->v2;
|
||||
sorted.push_front(reverseEdge(pEI->edge));
|
||||
edges.erase(pEI->it);
|
||||
edge_points.erase(pEI);
|
||||
pEI = edge_points.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pEI == edge_points.end()) || (last.SquareDistance(first) <= tol3d)) {
|
||||
// no adjacent edge found or polyline is closed
|
||||
return sorted;
|
||||
}
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
TopoShape &TopoShape::makeElementOrderedWires(const std::vector<TopoShape> &shapes,
|
||||
const char *op,
|
||||
double tol,
|
||||
TopoShapeMap *output)
|
||||
{
|
||||
if(!op) op = Part::OpCodes::Wire;
|
||||
if(tol<Precision::Confusion()) tol = Precision::Confusion();
|
||||
|
||||
std::vector<TopoShape> wires;
|
||||
std::list<TopoShape> edge_list;
|
||||
|
||||
auto shape = TopoShape().makeElementCompound(shapes, "", false);
|
||||
for(auto &e : shape.getSubTopoShapes(TopAbs_EDGE))
|
||||
edge_list.push_back(e);
|
||||
|
||||
while(edge_list.size()) {
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
std::vector<TopoShape> edges;
|
||||
for (auto &edge : sortEdges(edge_list, true, tol)) {
|
||||
edges.push_back(edge);
|
||||
mkWire.Add(TopoDS::Edge(edge.getShape()));
|
||||
// MakeWire will replace vertex of connected edge, which
|
||||
// effectively creat a new edge. So we need to update the shape
|
||||
// in order to preserve element mapping.
|
||||
edges.back().setShape(mkWire.Edge(), false);
|
||||
if (output)
|
||||
(*output)[edges.back()] = edge;
|
||||
}
|
||||
wires.push_back(mkWire.Wire());
|
||||
wires.back().mapSubElement(edges,op);
|
||||
}
|
||||
return makeElementCompound(wires,0,false);
|
||||
}
|
||||
|
||||
} // namespace Part
|
||||
|
||||
Reference in New Issue
Block a user