Merge pull request #12189 from CalligaroV/toponamingTopoShapeWire

Toponaming makeElementWire
This commit is contained in:
Chris Hennes
2024-02-08 20:59:26 -06:00
committed by GitHub
5 changed files with 789 additions and 11 deletions

View File

@@ -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())

View File

@@ -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

View File

@@ -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)
{

View File

@@ -112,6 +112,7 @@ enum class MappingStatus
Generated,
Modified
};
/** Shape mapper for user defined shape mapping
*/
struct PartExport ShapeMapper: TopoShape::Mapper

View File

@@ -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