Merge pull request #12189 from CalligaroV/toponamingTopoShapeWire
Toponaming makeElementWire
This commit is contained in:
@@ -3146,6 +3146,59 @@ void TopoShape::sewShape(double tolerance)
|
||||
this->_Shape = sew.SewedShape();
|
||||
}
|
||||
|
||||
bool TopoShape::fix()
|
||||
{
|
||||
if (this->_Shape.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First, we do fix regardless if the current shape is valid or not,
|
||||
// because not all problems that are handled by ShapeFix_Shape can be
|
||||
// recognized by BRepCheck_Analyzer.
|
||||
//
|
||||
// Second, for some reason, a failed fix (i.e. a fix that produces invalid shape)
|
||||
// will affect the input shape. (See // https://github.com/realthunder/FreeCAD/issues/585,
|
||||
// BTW, the file attached in the issue also shows that ShapeFix_Shape may
|
||||
// actually make a valid input shape invalid). So, it actually change the
|
||||
// underlying shape data. Therefore, we try with a copy first.
|
||||
auto copy = makeElementCopy();
|
||||
ShapeFix_Shape fix(copy._Shape);
|
||||
fix.Perform();
|
||||
|
||||
if (fix.Shape().IsSame(copy._Shape)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BRepCheck_Analyzer aChecker(fix.Shape());
|
||||
if (!aChecker.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the above fix produces a valid shape, then we fix the original shape,
|
||||
// because BRepBuilderAPI_Copy has some undesired side effect (e.g. flatten
|
||||
// underlying shape, and thus break internal shape sharing).
|
||||
ShapeFix_Shape fixThis(this->_Shape);
|
||||
fixThis.Perform();
|
||||
|
||||
aChecker.Init(fixThis.Shape());
|
||||
if (aChecker.IsValid()) {
|
||||
// Must call makESHAPE() (which calls mapSubElement()) to remap element
|
||||
// names because ShapeFix_Shape may delete (e.g. small edges) or modify
|
||||
// the input shape.
|
||||
//
|
||||
// See https://github.com/realthunder/FreeCAD/issues/595. Sketch001
|
||||
// has small edges. Simply recompute the sketch to trigger call of fix()
|
||||
// through makEWires(), and it will remove those edges. Without
|
||||
// remapping, there will be invalid index jumpping in reference in
|
||||
// Sketch002.ExternalEdge5.
|
||||
makeShapeWithElementMap(fixThis.Shape(), MapperHistory(fixThis), {*this});
|
||||
}
|
||||
else {
|
||||
makeShapeWithElementMap(fix.Shape(), MapperHistory(fix), {copy});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TopoShape::fix(double precision, double mintol, double maxtol)
|
||||
{
|
||||
if (this->_Shape.IsNull())
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
#include <BRepOffsetAPI_MakePipeShell.hxx>
|
||||
#include <BRepFeat_MakePrism.hxx>
|
||||
#include <BRepPrimAPI_MakeHalfSpace.hxx>
|
||||
#include <BRepTools_History.hxx>
|
||||
#include <BRepTools_ReShape.hxx>
|
||||
#include <ShapeFix_Root.hxx>
|
||||
|
||||
class gp_Ax1;
|
||||
class gp_Ax2;
|
||||
@@ -53,7 +56,10 @@ class Color;
|
||||
namespace Part
|
||||
{
|
||||
|
||||
struct ShapeHasher;
|
||||
class TopoShape;
|
||||
class TopoShapeCache;
|
||||
typedef std::unordered_map<TopoShape, TopoShape, ShapeHasher, ShapeHasher> TopoShapeMap;
|
||||
|
||||
/* A special sub-class to indicate null shapes
|
||||
*/
|
||||
@@ -393,6 +399,7 @@ public:
|
||||
TopoDS_Shape replaceShape(const std::vector<std::pair<TopoDS_Shape, TopoDS_Shape>>& s) const;
|
||||
TopoDS_Shape removeShape(const std::vector<TopoDS_Shape>& s) const;
|
||||
void sewShape(double tolerance = 1.0e-06);
|
||||
bool fix();
|
||||
bool fix(double, double, double);
|
||||
bool fixSolidOrientation();
|
||||
bool removeInternalWires(double);
|
||||
@@ -687,6 +694,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=nullptr);
|
||||
void mapSubElementsTo(std::vector<TopoShape>& shapes, const char* op = nullptr) const;
|
||||
bool hasPendingElementMap() const;
|
||||
|
||||
/** Helper class to return the generated and modified shape given an input shape
|
||||
@@ -727,22 +735,181 @@ public:
|
||||
const Mapper &mapper,
|
||||
const std::vector<TopoShape> &sources,
|
||||
const char *op=nullptr);
|
||||
/**
|
||||
* When given a single shape to create a compound, two results are possible: either to simply
|
||||
* return the shape as given, or to force it to be placed in a Compound.
|
||||
*/
|
||||
enum class SingleShapeCompoundCreationPolicy {
|
||||
returnShape,
|
||||
forceCompound
|
||||
};
|
||||
|
||||
/** Make a compound shape
|
||||
*
|
||||
* @param shapes: input shapes
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param force: if true and there is only one input shape, then return
|
||||
* that shape instead. If false, then always return a
|
||||
* compound, even if there is no input shape.
|
||||
* @param policy: set behavior when only a single shape is given
|
||||
*
|
||||
* @return 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 &makeElementCompound(const std::vector<TopoShape> &shapes, const char *op=nullptr, bool force=true);
|
||||
TopoShape& makeElementCompound(const std::vector<TopoShape>& shapes,
|
||||
const char* op = nullptr,
|
||||
SingleShapeCompoundCreationPolicy policy =
|
||||
SingleShapeCompoundCreationPolicy::forceCompound);
|
||||
|
||||
|
||||
enum class ConnectionPolicy
|
||||
{
|
||||
requireSharedVertex,
|
||||
mergeWithTolerance
|
||||
};
|
||||
|
||||
/** 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,
|
||||
ConnectionPolicy policy = ConnectionPolicy::mergeWithTolerance,
|
||||
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 policy: if requireSharedVertex, then only connect edges if they shared the same
|
||||
* vertex. If mergeWithTolerance 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,
|
||||
ConnectionPolicy policy = ConnectionPolicy::mergeWithTolerance,
|
||||
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,
|
||||
ConnectionPolicy policy = ConnectionPolicy::mergeWithTolerance,
|
||||
TopoShapeMap* output = nullptr) const
|
||||
{
|
||||
return TopoShape(0, Hasher).makeElementWires(*this, op, tol, policy, output);
|
||||
}
|
||||
|
||||
|
||||
/** Make a deep copy of the shape
|
||||
*
|
||||
* @param source: input shape
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param copyGeom: whether to copy internal geometry of the shape
|
||||
* @param copyMesh: whether to copy internal meshes of the shape
|
||||
*
|
||||
* @return The original content of this TopoShape is discarded and replaced
|
||||
* with a deep copy of the input shape. The function returns the
|
||||
* TopoShape itself as a self reference so that multiple operations
|
||||
* can be carried out for the same shape in the same line of code.
|
||||
*/
|
||||
TopoShape& makeElementCopy(const TopoShape& source,
|
||||
const char* op = nullptr,
|
||||
bool copyGeom = true,
|
||||
bool copyMesh = false);
|
||||
|
||||
/** Make a deep copy of the shape
|
||||
*
|
||||
* @param op: optional string to be encoded into topo naming for indicating
|
||||
* the operation
|
||||
* @param copyGeom: whether to copy internal geometry of the shape
|
||||
* @param copyMesh: whether to copy internal meshes of the shape
|
||||
*
|
||||
* @return Return a deep copy of the shape. The shape itself is not
|
||||
* modified
|
||||
*/
|
||||
TopoShape
|
||||
makeElementCopy(const char* op = nullptr, bool copyGeom = true, bool copyMesh = false) const
|
||||
{
|
||||
return TopoShape(Tag, Hasher).makeElementCopy(*this, op, copyGeom, copyMesh);
|
||||
}
|
||||
|
||||
/* Make a shell using this shape
|
||||
* @param silent: whether to throw exception on failure
|
||||
@@ -1223,9 +1390,22 @@ 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);
|
||||
static TopoShape reverseEdge(const TopoShape& edge);
|
||||
};
|
||||
|
||||
|
||||
/** Shape mapper for generic BRepBuilderAPI_MakeShape derived class
|
||||
*
|
||||
* Uses BRepBuilderAPI_MakeShape::Modified/Generated() function to extract
|
||||
@@ -1241,7 +1421,21 @@ struct PartExport MapperMaker: TopoShape::Mapper
|
||||
const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override;
|
||||
};
|
||||
|
||||
/** Shape mapper for BRepTools_History
|
||||
*
|
||||
* Uses BRepTools_History::Modified/Generated() function to extract
|
||||
* shape history for generating mapped element names
|
||||
*/
|
||||
struct PartExport MapperHistory: TopoShape::Mapper
|
||||
{
|
||||
Handle(BRepTools_History) history;
|
||||
explicit MapperHistory(const Handle(BRepTools_History) & history);
|
||||
explicit MapperHistory(const Handle(BRepTools_ReShape) & reshape);
|
||||
explicit MapperHistory(ShapeFix_Root& fix);
|
||||
const std::vector<TopoDS_Shape>& modified(const TopoDS_Shape& s) const override;
|
||||
const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override;
|
||||
};
|
||||
|
||||
} // namespace Part
|
||||
|
||||
|
||||
#endif // PART_TOPOSHAPE_H
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <cmath>
|
||||
|
||||
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <modelRefine.h>
|
||||
@@ -33,12 +35,17 @@
|
||||
#include <BRepTools.hxx>
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <ShapeBuild_ReShape.hxx>
|
||||
#include <ShapeFix_Shape.hxx>
|
||||
#include <ShapeFix_ShapeTolerance.hxx>
|
||||
#include <ShapeUpgrade_ShellSewing.hxx>
|
||||
#include <gp_Pln.hxx>
|
||||
|
||||
#include <utility>
|
||||
#include <BRepBuilderAPI_Copy.hxx>
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <TopTools_HSequenceOfShape.hxx>
|
||||
#include <ShapeAnalysis_FreeBounds.hxx>
|
||||
#endif
|
||||
|
||||
#include "TopoShape.h"
|
||||
@@ -453,6 +460,13 @@ 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)
|
||||
{
|
||||
@@ -1277,10 +1291,11 @@ void addShapesToBuilder(const std::vector<TopoShape>& shapes,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TopoShape&
|
||||
TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes, const char* op, bool force)
|
||||
TopoShape& TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes,
|
||||
const char* op,
|
||||
SingleShapeCompoundCreationPolicy policy)
|
||||
{
|
||||
if (!force && shapes.size() == 1) {
|
||||
if (policy == SingleShapeCompoundCreationPolicy::returnShape && shapes.size() == 1) {
|
||||
*this = shapes[0];
|
||||
return *this;
|
||||
}
|
||||
@@ -1301,6 +1316,337 @@ TopoShape::makeElementCompound(const std::vector<TopoShape>& shapes, const char*
|
||||
return *this;
|
||||
}
|
||||
|
||||
TopoShape& TopoShape::makeElementWires(const std::vector<TopoShape>& shapes,
|
||||
const char* op,
|
||||
double tol,
|
||||
ConnectionPolicy policy,
|
||||
TopoShapeMap* output)
|
||||
{
|
||||
if (shapes.empty()) {
|
||||
FC_THROWM(NullShapeException, "Null shape");
|
||||
}
|
||||
if (shapes.size() == 1) {
|
||||
return makeElementWires(shapes[0], op, tol, policy, output);
|
||||
}
|
||||
return makeElementWires(TopoShape(Tag).makeElementCompound(shapes), op, tol, policy, output);
|
||||
}
|
||||
|
||||
|
||||
TopoShape& TopoShape::makeElementWires(const TopoShape& shape,
|
||||
const char* op,
|
||||
double tol,
|
||||
ConnectionPolicy policy,
|
||||
TopoShapeMap* output)
|
||||
{
|
||||
if (!op) {
|
||||
op = Part::OpCodes::Wire;
|
||||
}
|
||||
if (tol < Precision::Confusion()) {
|
||||
tol = Precision::Confusion();
|
||||
}
|
||||
|
||||
if (policy == ConnectionPolicy::requireSharedVertex) {
|
||||
// Can't use ShapeAnalysis_FreeBounds if not shared. It seems the output
|
||||
// edges are modified somehow, 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() == 0) {
|
||||
FC_THROWM(NullShapeException, "Null shape");
|
||||
}
|
||||
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(hEdges, tol, Standard_True, hWires);
|
||||
if (hWires->Length() == 0) {
|
||||
FC_THROWM(NullShapeException, "Null shape");
|
||||
}
|
||||
|
||||
std::vector<TopoShape> wires;
|
||||
for (int i = 1; i <= hWires->Length(); i++) {
|
||||
auto wire = hWires->Value(i);
|
||||
wires.emplace_back(Tag, Hasher, wire);
|
||||
}
|
||||
shape.mapSubElementsTo(wires, op);
|
||||
return makeElementCompound(wires, "", SingleShapeCompoundCreationPolicy::returnShape);
|
||||
}
|
||||
|
||||
std::vector<TopoShape> wires;
|
||||
std::list<TopoShape> edgeList;
|
||||
|
||||
for (auto& edge : shape.getSubTopoShapes(TopAbs_EDGE)) {
|
||||
edgeList.emplace_back(edge);
|
||||
}
|
||||
|
||||
std::vector<TopoShape> edges;
|
||||
edges.reserve(edgeList.size());
|
||||
wires.reserve(edgeList.size());
|
||||
|
||||
// sort them together to wires
|
||||
while (!edgeList.empty()) {
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
// add and erase first edge
|
||||
edges.clear();
|
||||
edges.push_back(edgeList.front());
|
||||
mkWire.Add(TopoDS::Edge(edges.back().getShape()));
|
||||
edges.back().setShape(mkWire.Edge(), false);
|
||||
if (output) {
|
||||
(*output)[edges.back()] = edgeList.front();
|
||||
}
|
||||
edgeList.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 = true;
|
||||
while (found) {
|
||||
found = false;
|
||||
for (auto it = edgeList.begin(); it != edgeList.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;
|
||||
}
|
||||
edgeList.erase(it);
|
||||
new_wire = mkWire.Wire();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wires.emplace_back(new_wire);
|
||||
wires.back().mapSubElement(edges, op);
|
||||
wires.back().fix();
|
||||
}
|
||||
return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::returnShape);
|
||||
}
|
||||
|
||||
|
||||
struct EdgePoints
|
||||
{
|
||||
gp_Pnt v1, v2;
|
||||
std::list<TopoShape>::iterator it;
|
||||
const TopoShape* edge;
|
||||
bool closed {false};
|
||||
|
||||
EdgePoints(std::list<TopoShape>::iterator it, double tol)
|
||||
: it(it)
|
||||
, edge(&*it)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TopoShape TopoShape::reverseEdge(const TopoShape& edge)
|
||||
{
|
||||
Standard_Real first = NAN;
|
||||
Standard_Real last = NAN;
|
||||
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, res.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, res.Tag);
|
||||
res.elementMap()->setElementName(v2Name, v1, res.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, res.Tag);
|
||||
}
|
||||
else if (v1) {
|
||||
res.elementMap()->setElementName(v2Name, v1, res.Tag);
|
||||
}
|
||||
else if (v2) {
|
||||
res.elementMap()->setElementName(v1Name, v2, res.Tag);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
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> edgePoints;
|
||||
for (auto it = edges.begin(); it != edges.end(); ++it) {
|
||||
edgePoints.emplace_back(it, tol3d);
|
||||
}
|
||||
|
||||
std::deque<TopoShape> sorted;
|
||||
if (edgePoints.empty()) {
|
||||
return sorted;
|
||||
}
|
||||
|
||||
gp_Pnt first;
|
||||
gp_Pnt last;
|
||||
first = edgePoints.front().v1;
|
||||
last = edgePoints.front().v2;
|
||||
|
||||
sorted.push_back(*edgePoints.front().edge);
|
||||
edges.erase(edgePoints.front().it);
|
||||
if (edgePoints.front().closed) {
|
||||
return sorted;
|
||||
}
|
||||
|
||||
edgePoints.erase(edgePoints.begin());
|
||||
|
||||
while (!edgePoints.empty()) {
|
||||
// search for adjacent edge
|
||||
std::list<EdgePoints>::iterator pEI;
|
||||
for (pEI = edgePoints.begin(); pEI != edgePoints.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);
|
||||
edgePoints.erase(pEI);
|
||||
pEI = edgePoints.begin();
|
||||
break;
|
||||
}
|
||||
if (pEI->v2.SquareDistance(first) <= tol3d) {
|
||||
sorted.push_front(*pEI->edge);
|
||||
first = pEI->v1;
|
||||
edges.erase(pEI->it);
|
||||
edgePoints.erase(pEI);
|
||||
pEI = edgePoints.begin();
|
||||
break;
|
||||
}
|
||||
if (pEI->v2.SquareDistance(last) <= tol3d) {
|
||||
last = pEI->v1;
|
||||
sorted.push_back(reverseEdge(*pEI->edge));
|
||||
edges.erase(pEI->it);
|
||||
edgePoints.erase(pEI);
|
||||
pEI = edgePoints.begin();
|
||||
break;
|
||||
}
|
||||
if (pEI->v1.SquareDistance(first) <= tol3d) {
|
||||
first = pEI->v2;
|
||||
sorted.push_front(reverseEdge(*pEI->edge));
|
||||
edges.erase(pEI->it);
|
||||
edgePoints.erase(pEI);
|
||||
pEI = edgePoints.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pEI == edgePoints.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> edgeList;
|
||||
|
||||
auto shape =
|
||||
TopoShape().makeElementCompound(shapes, "", SingleShapeCompoundCreationPolicy::returnShape);
|
||||
for (auto& edge : shape.getSubTopoShapes(TopAbs_EDGE)) {
|
||||
edgeList.push_back(edge);
|
||||
}
|
||||
|
||||
while (!edgeList.empty()) {
|
||||
BRepBuilderAPI_MakeWire mkWire;
|
||||
std::vector<TopoShape> edges;
|
||||
for (auto& edge : sortEdges(edgeList, 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.emplace_back(mkWire.Wire());
|
||||
wires.back().mapSubElement(edges, op);
|
||||
}
|
||||
return makeElementCompound(wires, nullptr, SingleShapeCompoundCreationPolicy::returnShape);
|
||||
}
|
||||
|
||||
|
||||
TopoShape&
|
||||
TopoShape::makeElementCopy(const TopoShape& shape, const char* op, bool copyGeom, bool copyMesh)
|
||||
{
|
||||
if (shape.isNull()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
TopoShape tmp(shape);
|
||||
#if OCC_VERSION_HEX >= 0x070000
|
||||
tmp.setShape(BRepBuilderAPI_Copy(shape.getShape(), copyGeom, copyMesh).Shape(), false);
|
||||
#else
|
||||
tmp.setShape(BRepBuilderAPI_Copy(shape.getShape()).Shape(), false);
|
||||
#endif
|
||||
if (op || (shape.Tag && shape.Tag != Tag)) {
|
||||
setShape(tmp._Shape);
|
||||
initCache();
|
||||
if (!Hasher) {
|
||||
Hasher = tmp.Hasher;
|
||||
}
|
||||
copyElementMap(tmp, op);
|
||||
}
|
||||
else {
|
||||
*this = tmp;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
struct MapperSewing: Part::TopoShape::Mapper
|
||||
{
|
||||
BRepBuilderAPI_Sewing& maker;
|
||||
@@ -1792,6 +2138,62 @@ const std::vector<TopoDS_Shape>& MapperMaker::generated(const TopoDS_Shape& s) c
|
||||
return _res;
|
||||
}
|
||||
|
||||
MapperHistory::MapperHistory(const Handle(BRepTools_History) & history)
|
||||
: history(history)
|
||||
{}
|
||||
|
||||
MapperHistory::MapperHistory(const Handle(BRepTools_ReShape) & reshape)
|
||||
{
|
||||
if (reshape) {
|
||||
history = reshape->History();
|
||||
}
|
||||
}
|
||||
|
||||
MapperHistory::MapperHistory(ShapeFix_Root& fix)
|
||||
{
|
||||
if (fix.Context()) {
|
||||
history = fix.Context()->History();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<TopoDS_Shape>& MapperHistory::modified(const TopoDS_Shape& s) const
|
||||
{
|
||||
_res.clear();
|
||||
try {
|
||||
if (history) {
|
||||
TopTools_ListIteratorOfListOfShape it;
|
||||
for (it.Initialize(history->Modified(s)); it.More(); it.Next()) {
|
||||
_res.push_back(it.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Standard_Failure& e) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Exception on shape mapper: " << e.GetMessageString());
|
||||
}
|
||||
}
|
||||
return _res;
|
||||
}
|
||||
|
||||
const std::vector<TopoDS_Shape>& MapperHistory::generated(const TopoDS_Shape& s) const
|
||||
{
|
||||
_res.clear();
|
||||
try {
|
||||
if (history) {
|
||||
TopTools_ListIteratorOfListOfShape it;
|
||||
for (it.Initialize(history->Generated(s)); it.More(); it.Next()) {
|
||||
_res.push_back(it.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Standard_Failure& e) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
FC_WARN("Exception on shape mapper: " << e.GetMessageString());
|
||||
}
|
||||
}
|
||||
return _res;
|
||||
}
|
||||
|
||||
// topo naming counterpart of TopoShape::makeShell()
|
||||
TopoShape& TopoShape::makeElementShell(bool silent, const char* op)
|
||||
{
|
||||
|
||||
@@ -112,6 +112,7 @@ enum class MappingStatus
|
||||
Generated,
|
||||
Modified
|
||||
};
|
||||
|
||||
/** Shape mapper for user defined shape mapping
|
||||
*/
|
||||
struct PartExport ShapeMapper: TopoShape::Mapper
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_Transform.hxx>
|
||||
#include <BRepFeat_SplitShape.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepAlgoAPI_Fuse.hxx>
|
||||
#include <GC_MakeCircle.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
@@ -54,7 +57,10 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeReturnsShape)
|
||||
std::vector<Part::TopoShape> shapes {topoShape};
|
||||
|
||||
// Act
|
||||
topoShape.makeElementCompound(shapes, "C", false /*Don't force the creation*/);
|
||||
topoShape.makeElementCompound(shapes,
|
||||
"C",
|
||||
Part::TopoShape::SingleShapeCompoundCreationPolicy::
|
||||
returnShape /*Don't force the creation*/);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(edge.ShapeType(), topoShape.getShape().ShapeType()); // NOT a Compound
|
||||
@@ -68,7 +74,10 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundOneShapeForceReturnsCompound)
|
||||
std::vector<Part::TopoShape> shapes {topoShape};
|
||||
|
||||
// Act
|
||||
topoShape.makeElementCompound(shapes, "C", true /*Force the creation*/);
|
||||
topoShape.makeElementCompound(
|
||||
shapes,
|
||||
"C",
|
||||
Part::TopoShape::SingleShapeCompoundCreationPolicy::forceCompound /*Force the creation*/);
|
||||
|
||||
// Assert
|
||||
EXPECT_NE(edge.ShapeType(), topoShape.getShape().ShapeType()); // No longer the same thing
|
||||
@@ -146,6 +155,125 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes)
|
||||
// 26 subshapes each
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, MapperMakerModified)
|
||||
{
|
||||
// Arrange
|
||||
// Definition of all the objects needed for a Transformation
|
||||
// (https://dev.opencascade.org/doc/refman/html/class_b_rep_builder_a_p_i___transform.html)
|
||||
auto translation {gp_Trsf()};
|
||||
auto transform {BRepBuilderAPI_Transform(translation)};
|
||||
auto transformMprMkr {MapperMaker(transform)};
|
||||
|
||||
// Definition of all the objects needed for a Shape Splitting
|
||||
// (https://dev.opencascade.org/doc/refman/html/class_b_rep_feat___split_shape.html)
|
||||
auto splitMkr {BRepFeat_SplitShape()};
|
||||
auto splitMprMkr {MapperMaker(splitMkr)};
|
||||
|
||||
// Creating a Wire, used later to create a Face
|
||||
auto wireMkr {BRepBuilderAPI_MakeWire(
|
||||
BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)),
|
||||
BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)),
|
||||
BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 1.0, 0.0), gp_Pnt(0.0, 0.0, 0.0)))};
|
||||
auto wire = wireMkr.Wire();
|
||||
|
||||
// Creating a Face using the Wire created before
|
||||
auto faceMkr {BRepBuilderAPI_MakeFace(wire)};
|
||||
auto face = faceMkr.Face();
|
||||
|
||||
// Creating an Edge to split the Face and the Wire
|
||||
auto edgeMkr {BRepBuilderAPI_MakeEdge(gp_Pnt(0.5, 1.0, 0.0), gp_Pnt(0.5, -1.0, 0.0))};
|
||||
auto edge = edgeMkr.Edge();
|
||||
|
||||
// Act
|
||||
// Performing the Transformation
|
||||
translation.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0));
|
||||
transform.Perform(wire);
|
||||
|
||||
// Initializing and performing the Split
|
||||
splitMkr.Init(face);
|
||||
splitMkr.Add(edge, face);
|
||||
splitMkr.Build();
|
||||
|
||||
// Assert
|
||||
// Check that all the shapes and operations have been performed
|
||||
EXPECT_TRUE(wireMkr.IsDone());
|
||||
EXPECT_TRUE(faceMkr.IsDone());
|
||||
EXPECT_TRUE(edgeMkr.IsDone());
|
||||
EXPECT_TRUE(splitMkr.IsDone());
|
||||
EXPECT_TRUE(transform.IsDone());
|
||||
|
||||
// Check the result of the operations
|
||||
EXPECT_EQ(transformMprMkr.modified(wire).size(), 1); // The Transformation acts on the Wire...
|
||||
EXPECT_EQ(transformMprMkr.modified(face).size(), 1); // ... and therefor on the created Face...
|
||||
EXPECT_EQ(transformMprMkr.modified(edge).size(), 1); // ... and on the Edge added to the Face
|
||||
|
||||
EXPECT_EQ(splitMprMkr.modified(edge).size(), 0); // The Split doesn't modify the Edge
|
||||
EXPECT_EQ(splitMprMkr.modified(wire).size(), 2); // The Split modifies the Wire into 2 Wires
|
||||
EXPECT_EQ(splitMprMkr.modified(face).size(), 2); // The Split modifies the Face into 2 Faces
|
||||
}
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, MapperMakerGenerated)
|
||||
{
|
||||
// Arrange
|
||||
// Creating tree Edges, used later in the Fuse operations
|
||||
auto edge1 {BRepBuilderAPI_MakeEdge(gp_Pnt(-1.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge()};
|
||||
auto edge2 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, -1.0, 0.0), gp_Pnt(0.0, 1.0, 0.0)).Edge()};
|
||||
auto edge3 {BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, -1.0), gp_Pnt(0.0, 0.0, 1.0)).Edge()};
|
||||
|
||||
// Definition of all the objects needed for the Fuses
|
||||
// (https://dev.opencascade.org/doc/refman/html/class_b_rep_algo_a_p_i___fuse.html)
|
||||
// The Fuse operation, like other Boolean operations derived from BRepAlgoAPI_BuilderAlgo,
|
||||
// supports the generation history
|
||||
// (https://dev.opencascade.org/doc/refman/html/class_b_rep_algo_a_p_i___builder_algo.html)
|
||||
auto fuse1Mkr {BRepAlgoAPI_Fuse(edge1, edge2)};
|
||||
auto fuse1MprMkr {MapperMaker(fuse1Mkr)};
|
||||
auto fuse2Mkr {BRepAlgoAPI_Fuse(edge1, edge3)};
|
||||
auto fuse2MprMkr {MapperMaker(fuse2Mkr)};
|
||||
|
||||
// Act
|
||||
fuse1Mkr.Build();
|
||||
fuse2Mkr.Build();
|
||||
|
||||
// Assert
|
||||
// Check that all the shapes and operations have been performed
|
||||
EXPECT_TRUE(fuse1Mkr.IsDone());
|
||||
EXPECT_TRUE(fuse2Mkr.IsDone());
|
||||
|
||||
// Check the result of the operations
|
||||
EXPECT_EQ(fuse1MprMkr.generated(edge1).size(), 1); // fuse1 has a new vertex generated by edge1
|
||||
EXPECT_EQ(fuse1MprMkr.generated(edge2).size(), 1); // fuse1 has a new vertex generated by edge2
|
||||
EXPECT_EQ(fuse1MprMkr.generated(edge3).size(),
|
||||
0); // fuse1 doesn't have a new vertex generated by edge3
|
||||
|
||||
EXPECT_EQ(fuse2MprMkr.generated(edge1).size(), 1); // fuse2 has a new vertex generated by edge1
|
||||
EXPECT_EQ(fuse2MprMkr.generated(edge2).size(),
|
||||
0); // fuse2 doesn't have a new vertex generated by edge2
|
||||
EXPECT_EQ(fuse2MprMkr.generated(edge3).size(), 1); // fuse2 has a new vertex generated by edge3
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
// The following test has been disabled to avoid the CI failing
|
||||
// will be enabled again in following PRs
|
||||
// ================================================================================================
|
||||
|
||||
// TEST_F(TopoShapeExpansionTest, makeElementWiresCombinesAdjacent)
|
||||
// {
|
||||
// // Arrange
|
||||
// auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge();
|
||||
// auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(2.0, 0.0, 0.0)).Edge();
|
||||
// Part::TopoShape topoShape;
|
||||
// std::vector<Part::TopoShape> shapes {edge1, edge2};
|
||||
|
||||
// // Act
|
||||
// topoShape.makeElementWires(shapes);
|
||||
|
||||
// // Assert
|
||||
// auto elementMap = topoShape.getElementMap();
|
||||
// EXPECT_EQ(6, elementMap.size());
|
||||
// }
|
||||
|
||||
// ================================================================================================
|
||||
|
||||
TEST_F(TopoShapeExpansionTest, makeElementFaceNull)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
Reference in New Issue
Block a user