Toponaming/Part: Transfer in makERevolve, makEPrism, makEPrismUntil

This commit is contained in:
Zheng, Lei
2024-02-23 12:13:47 -05:00
committed by bgbsww
parent 8a3407aee6
commit c3d152540c
2 changed files with 478 additions and 0 deletions

View File

@@ -258,6 +258,12 @@ public:
void operator=(const TopoShape&);
bool operator == (const TopoShape &other) const {
return _Shape.IsEqual(other._Shape);
}
virtual bool isSame (const Data::ComplexGeoData &other) const;
/** @name Placement control */
//@{
/// set the transformation of the CasCade Shape
@@ -858,6 +864,144 @@ public:
offsetMode,join,op);
}
/** Make revolved shell around a basis shape
*
* @param base: the base shape
* @param axis: the revolving axis
* @param d: rotation angle in degree
* @param face_maker: optional type name of the the maker used to make a
* face from basis shape
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return The original content of this TopoShape is discarded and replaced
* with the new 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 &makeElementRevolve(const TopoShape &base, const gp_Ax1& axis, double d,
const char *face_maker=0, const char *op=nullptr);
/** Make revolved shell around a basis shape
*
* @param base: the basis shape
* @param axis: the revolving axis
* @param d: rotation angle in degree
* @param face_maker: optional type name of the the maker used to make a
* face from basis shape
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
TopoShape makeElementRevolve(const gp_Ax1& axis, double d,
const char *face_maker=nullptr, const char *op=nullptr) const {
return TopoShape(0,Hasher).makeElementRevolve(*this,axis,d,face_maker,op);
}
/** Make a prism that is a linear sweep of a basis shape
*
* @param base: the basis shape
* @param vec: vector defines the sweep direction
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return The original content of this TopoShape is discarded and replaced
* with the new 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 &makeElementPrism(const TopoShape &base, const gp_Vec& vec, const char *op=nullptr);
/** Make a prism that is a linear sweep of this shape
*
* @param vec: vector defines the sweep direction
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
TopoShape makeElementPrism(const gp_Vec& vec, const char *op=nullptr) const {
return TopoShape(0,Hasher).makeElementPrism(*this,vec,op);
}
/// Operation mode for makeElementPrismUntil()
enum PrismMode {
/// Remove the generated prism shape from the base shape with boolean cut
CutFromBase = 0,
/// Add generated prism shape to the base shape with fusion
FuseWithBase = 1,
/// Return the generated prism shape without base shape
None = 2
};
/** Make a prism that is either depression or protrusion of a profile shape up to a given face
*
* @param base: the base shape
* @param profile: profile shape used for sweeping to make the prism
* @param supportFace: optional face serves to determining the type of
* operation. If it is inside the basis shape, a local
* operation such as glueing can be performed.
* @param upToFace: sweep the profile up until this give face.
* @param direction: the direction to sweep the profile
* @param mode: defines what shape to return. @sa PrismMode
* @param checkLimits: If true, then remove limit (boundary) of up to face.
* If false, then the generate prism may go beyond the
* boundary of the up to face.
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return The original content of this TopoShape is discarded and replaced
* with the new 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 &makeElementPrismUntil(const TopoShape &base,
const TopoShape& profile,
const TopoShape& supportFace,
const TopoShape& upToFace,
const gp_Dir& direction,
PrismMode mode,
Standard_Boolean checkLimits = Standard_True,
const char *op=nullptr);
/** Make a prism based on this shape that is either depression or protrusion of a profile shape up to a given face
*
* @param profile: profile shape used for sweeping to make the prism
* @param supportFace: optional face serves to determining the type of
* operation. If it is inside the basis shape, a local
* operation such as glueing can be performed.
* @param upToFace: sweep the profile up until this give face.
* @param direction: the direction to sweep the profile
* @param mode: defines what shape to return. @sa PrismMode
* @param checkLimits: If true, then remove limit (boundary) of up to face.
* If false, then the generate prism may go beyond the
* boundary of the up to face.
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return Return the generated new shape. The TopoShape itself is not modified.
*/
TopoShape makeElementPrismUntil(const TopoShape& profile,
const TopoShape& supportFace,
const TopoShape& upToFace,
const gp_Dir& direction,
PrismMode mode,
Standard_Boolean checkLimits = Standard_True,
const char *op=nullptr) const
{
return TopoShape(0,Hasher).makeElementPrismUntil(*this,
profile,
supportFace,
upToFace,
direction,
mode,
checkLimits,
op);
}
/* Make a shell or solid by sweeping profile wire along a spine
*
* @params sources: source shapes. The first shape is used as spine. The
@@ -1838,6 +1982,22 @@ public:
return TopoShape(0, Hasher).makeElementShape(mkShape, *this, op);
}
/** Specialized shape making for BRepBuilderAPI_MakePrism with mapped element name
*
* @param mkShape: OCCT shape maker.
* @param sources: list of source shapes.
* @param op: optional string to be encoded into topo naming for indicating
* the operation
*
* @return The original content of this TopoShape is discarded and replaced
* with the new shape built by the shape maker. 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 &makeElementShape(BRepFeat_MakePrism &mkShape,
const std::vector<TopoShape> &sources, const TopoShape &uptoface, const char *op);
/* Toponaming migration, February 2014:
* Note that the specialized versions of makeElementShape for operations that do not
* inherit from BRepBuilderAPI_MakeShape ( like BRepBuilderAPI_Sewing ) have been removed.

View File

@@ -86,6 +86,10 @@
#include "Geometry.h"
#include <App/ElementNamingUtils.h>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepProj_Projection.hxx>
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
@@ -2701,6 +2705,103 @@ struct MapperThruSections: MapperMaker
}
};
struct MapperPrism: MapperMaker {
std::unordered_map<TopoDS_Shape, TopoDS_Shape, ShapeHasher, ShapeHasher> vertexMap;
ShapeMapper::ShapeMap edgeMap;
MapperPrism(BRepFeat_MakePrism &maker, const TopoShape &upTo)
:MapperMaker(maker)
{
(void)upTo;
std::vector<TopoShape> shapes;
for(TopTools_ListIteratorOfListOfShape it(maker.FirstShape());it.More();it.Next())
shapes.push_back(it.Value());
if (shapes.size()) {
// It seems that BRepFeat_MakePrism::newEdges() does not return
// edges generated by extruding the profile vertices. The following
// code assumes BRepFeat_MakePrism::myFShape is the profile, and
// FirstShape() returns the corresponding faces in the new shape,
// i.e. the bottom profile, and add all edges that shares a
// vertex with the profiles as new edges.
std::unordered_set<TopoDS_Shape,ShapeHasher,ShapeHasher> edgeSet;
TopoShape bottom;
bottom.makeElementCompound(shapes, nullptr, TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
TopoShape shape(maker.Shape());
for (auto &vertex : bottom.getSubShapes(TopAbs_VERTEX)) {
for (auto &e : shape.findAncestorsShapes(vertex, TopAbs_EDGE)) {
// Make sure to not visit the the same edge twice.
// And check only edge that are not found in the bottom profile
if (!edgeSet.insert(e).second && !bottom.findShape(e)) {
auto otherVertex = TopExp::FirstVertex(TopoDS::Edge(e));
if (otherVertex.IsSame(vertex))
otherVertex = TopExp::LastVertex(TopoDS::Edge(e));
vertexMap[vertex] = otherVertex;
}
}
}
// Now map each edge in the bottom profile to the extrueded top
// profile. vertexMap created above gives us each pair of vertexes
// of the bottom and top profile. We use it to find the
// corresponding edges in the top profile, what an extra criteria
// for disambiguation. That is, the pair of edges (bottom and top)
// must belong to the same face.
for (auto &edge : bottom.getSubShapes(TopAbs_EDGE)) {
std::vector<int> indices;
auto first = TopExp::FirstVertex(TopoDS::Edge(edge));
auto last = TopExp::LastVertex(TopoDS::Edge(edge));
auto itFirst = vertexMap.find(first);
auto itLast = vertexMap.find(last);
if (itFirst == vertexMap.end() || itLast ==vertexMap.end())
continue;
std::vector<TopoShape> faces;
for (int idx : shape.findAncestors(edge, TopAbs_FACE))
faces.push_back(shape.getSubTopoShape(TopAbs_FACE, idx));
if (faces.empty())
continue;
for (int idx : shape.findAncestors(itFirst->second, TopAbs_EDGE)) {
auto e = shape.getSubTopoShape(TopAbs_EDGE, idx);
if (!e.findShape(itLast->second))
continue;
for (auto &face : faces) {
if (!face.findShape(e.getShape()))
continue;
auto &entry = edgeMap[edge];
if (entry.shapeSet.insert(e.getShape()).second)
entry.shapes.push_back(e.getShape());
}
}
}
}
}
virtual const std::vector<TopoDS_Shape> &generated(const TopoDS_Shape &s) const override {
_res.clear();
switch(s.ShapeType()) {
case TopAbs_VERTEX: {
auto it = vertexMap.find(s);
if (it != vertexMap.end()) {
_res.push_back(it->second);
return _res;
}
break;
}
case TopAbs_EDGE: {
auto it = edgeMap.find(s);
if (it != edgeMap.end())
return it->second.shapes;
break;
}
default:
break;
}
MapperMaker::generated(s);
return _res;
}
};
// TODO: This method does not appear to ever be called in the codebase, and it is probably
// broken, because using TopoShape() with no parameters means the result will not have an
// element Map.
@@ -2917,6 +3018,18 @@ TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape,
return makeShapeWithElementMap(mkShape.Shape(), MapperMaker(mkShape), shapes, op);
}
TopoShape &TopoShape::makeElementShape(BRepFeat_MakePrism &mkShape,
const std::vector<TopoShape> &sources,
const TopoShape &upTo,
const char *op)
{
if(!op) op = Part::OpCodes::Prism;
MapperPrism mapper(mkShape, upTo);
makeShapeWithElementMap(mkShape.Shape(),mapper,sources,op);
return *this;
}
TopoShape& TopoShape::makeElementLoft(const std::vector<TopoShape>& shapes,
IsSolid isSolid,
IsRuled isRuled,
@@ -2984,6 +3097,200 @@ TopoShape& TopoShape::makeElementLoft(const std::vector<TopoShape>& shapes,
op);
}
TopoShape &TopoShape::makeElementPrism(const TopoShape &base, const gp_Vec& vec, const char *op) {
if(!op) op = Part::OpCodes::Extrude;
if(base.isNull())
FC_THROWM(NullShapeException, "Null shape");
BRepPrimAPI_MakePrism mkPrism(base.getShape(), vec);
return makeElementShape(mkPrism,base,op);
}
TopoShape &TopoShape::makeElementPrismUntil(const TopoShape &_base,
const TopoShape& profile,
const TopoShape& supportFace,
const TopoShape& __uptoface,
const gp_Dir& direction,
PrismMode Mode,
Standard_Boolean checkLimits,
const char *op)
{
if(!op) op = Part::OpCodes::Prism;
BRepFeat_MakePrism PrismMaker;
TopoShape _uptoface(__uptoface);
if (checkLimits && _uptoface.shapeType(true) == TopAbs_FACE
&& !BRep_Tool::NaturalRestriction(TopoDS::Face(_uptoface.getShape()))) {
// When using the face with BRepFeat_MakePrism::Perform(const TopoDS_Shape& Until)
// then the algorithm expects that the 'NaturalRestriction' flag is set in order
// to work as expected.
BRep_Builder builder;
_uptoface = _uptoface.makeElementCopy();
builder.NaturalRestriction(TopoDS::Face(_uptoface.getShape()), Standard_True);
}
TopoShape uptoface(_uptoface);
TopoShape base(_base);
if (base.isNull()) {
Mode = PrismMode::None;
base = profile;
}
// Check whether the face has limits or not. Unlimited faces have no wire
// Note: Datum planes are always unlimited
if (checkLimits && uptoface.hasSubShape(TopAbs_WIRE)) {
TopoDS_Face face = TopoDS::Face(uptoface.getShape());
bool remove_limits = false;
// Remove the limits of the upToFace so that the extrusion works even if profile is larger
// than the upToFace
for (auto &sketchface : profile.getSubTopoShapes(TopAbs_FACE)) {
// Get outermost wire of sketch face
TopoShape outerWire = sketchface.splitWires();
BRepProj_Projection proj(TopoDS::Wire(outerWire.getShape()), face, direction);
if (!proj.More() || !proj.Current().Closed()) {
remove_limits = true;
break;
}
}
// It must also be checked that all projected inner wires of the upToFace
// lie outside the sketch shape. If this is not the case then the sketch
// shape is not completely covered by the upToFace. See #0003141
if (!remove_limits) {
std::vector<TopoShape> wires;
uptoface.splitWires(&wires);
for (auto & w : wires) {
BRepProj_Projection proj(TopoDS::Wire(w.getShape()), profile.getShape(), -direction);
if (proj.More()) {
remove_limits = true;
break;
}
}
}
if (remove_limits) {
// Note: Using an unlimited face every time gives unnecessary failures for concave faces
TopLoc_Location loc = face.Location();
BRepAdaptor_Surface adapt(face, Standard_False);
// use the placement of the adapter, not of the upToFace
loc = TopLoc_Location(adapt.Trsf());
BRepBuilderAPI_MakeFace mkFace(adapt.Surface().Surface()
#if OCC_VERSION_HEX >= 0x060502
, Precision::Confusion()
#endif
);
if (!mkFace.IsDone())
remove_limits = false;
else
uptoface.setShape(located(mkFace.Shape(),loc), false);
}
}
TopoShape uptofaceCopy = uptoface;
bool checkBase = false;
auto retry = [&]() {
if (!uptoface.isSame(_uptoface)) {
// retry using the original up to face in case unnecessary failure
// due to removing the limits
uptoface = _uptoface;
return true;
}
if ((!_base.isNull() && base.isSame(_base))
|| (_base.isNull() && base.isSame(profile))) {
// It is unclear under exactly what condition extrude up to face
// can fail. Either the support face or the up to face must be part
// of the base, or maybe some thing else.
//
// To deal with it, we retry again by disregard the supplied base,
// and use up to face to extrude our own base. Later on, use the
// supplied base (i.e. _base) to calculate the final shape if the
// mode is FuseWithBase or CutWithBase.
checkBase = true;
uptoface = uptofaceCopy;
base.makeElementPrism(_uptoface, direction);
return true;
}
return false;
};
std::vector<TopoShape> srcShapes;
TopoShape result;
for (;;) {
try {
result = base;
// We do not rely on BRepFeat_MakePrism to perform fuse or cut for
// us because of its poor support of shape history.
auto mode = PrismMode::None;
for (auto &face : profile.getSubTopoShapes(
profile.hasSubShape(TopAbs_FACE)?TopAbs_FACE:TopAbs_WIRE)) {
srcShapes.clear();
if (!profile.isNull() && !result.findShape(profile.getShape()))
srcShapes.push_back(profile);
if (!supportFace.isNull() && !result.findShape(supportFace.getShape()))
srcShapes.push_back(supportFace);
// DO NOT include uptoface for element mapping. Because OCCT
// BRepFeat_MakePrism will report all top extruded face being
// modified by the uptoface. If there are more than one face in
// the profile, this will cause uncessary duplicated element
// mapped name. And will also disrupte element history tracing
// back to the profile sketch.
//
// if (!uptoface.isNull() && !this->findShape(uptoface.getShape()))
// srcShapes.push_back(uptoface);
srcShapes.push_back(result);
PrismMaker.Init(result.getShape(), face.getShape(),
TopoDS::Face(supportFace.getShape()), direction, mode, Standard_False);
mode = PrismMode::FuseWithBase;
PrismMaker.Perform(uptoface.getShape());
if (!PrismMaker.IsDone() || PrismMaker.Shape().IsNull())
FC_THROWM(Base::CADKernelError,"BRepFeat_MakePrism: extrusion failed");
result.makeElementShape(PrismMaker, srcShapes, uptoface, op);
}
break;
} catch (Base::Exception &) {
if (!retry()) throw;
} catch (Standard_Failure &) {
if (!retry()) throw;
}
}
if (!_base.isNull() && Mode != PrismMode::None) {
if (Mode == PrismMode::FuseWithBase)
result.makeElementFuse({_base, result});
else
result.makeElementCut({_base, result});
}
*this = result;
return *this;
}
TopoShape &TopoShape::makeElementRevolve(const TopoShape &_base, const gp_Ax1& axis,
double d, const char *face_maker, const char *op)
{
if(!op) op = Part::OpCodes::Revolve;
TopoShape base(_base);
if(base.isNull())
FC_THROWM(NullShapeException, "Null shape");
if(face_maker && !base.hasSubShape(TopAbs_FACE)) {
if(!base.hasSubShape(TopAbs_WIRE))
base = base.makeElementWires();
base = base.makeElementFace(nullptr,face_maker, nullptr);
}
BRepPrimAPI_MakeRevol mkRevol(base.getShape(), axis,d);
return makeElementShape(mkRevol,base,op);
}
TopoShape& TopoShape::makeElementDraft(const TopoShape& shape,
const std::vector<TopoShape>& _faces,
const gp_Dir& pullDirection,
@@ -3898,4 +4205,15 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
return *this;
}
bool TopoShape::isSame(const Data::ComplexGeoData &_other) const
{
if(!_other.isDerivedFrom(TopoShape::getClassTypeId()))
return false;
const auto &other = static_cast<const TopoShape &>(_other);
return Tag == other.Tag
&& Hasher == other.Hasher
&& _Shape.IsEqual(other._Shape);
}
} // namespace Part