Merge branch 'main' into bgbsww-toponamingMakeElementShape

This commit is contained in:
bgbsww
2024-01-28 11:19:11 -05:00
committed by GitHub
42 changed files with 2257 additions and 285 deletions

View File

@@ -25,21 +25,28 @@
#include "PreCompiled.h"
#ifndef _PreComp_
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <BRepFill_Generator.hxx>
#include <BRepTools.hxx>
#include <BRep_Builder.hxx>
#include <Precision.hxx>
#include <ShapeFix_Shape.hxx>
#include <ShapeFix_ShapeTolerance.hxx>
#include <ShapeUpgrade_ShellSewing.hxx>
#include <gp_Pln.hxx>
#include <utility>
#endif
#include "TopoShape.h"
#include "TopoShapeCache.h"
#include "TopoShapeOpCode.h"
#include "FaceMaker.h"
#include "TopoShapeOpCode.h"
#include <App/ElementNamingUtils.h>
FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT
@@ -336,6 +343,8 @@ void checkAndMatchHasher(TopoShape& topoShape1, const TopoShape& topoShape2)
}
} // namespace
// TODO: Refactor mapSubElementTypeForShape to reduce complexity
void TopoShape::mapSubElementTypeForShape(const TopoShape& other,
TopAbs_ShapeEnum type,
const char* op,
@@ -385,8 +394,8 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other,
}
std::ostringstream ss;
char elementType {shapeName(type)[0]};
if ( ! elementMap() ) {
FC_THROWM(NullShapeException, "No element map");
if (!elementMap()) {
FC_THROWM(NullShapeException, "No element map"); // NOLINT
}
elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag);
elementMap()->setElementName(element, name, Tag, &sids);
@@ -507,6 +516,743 @@ void TopoShape::mapSubElement(const std::vector<TopoShape>& shapes, const char*
}
}
struct ShapeInfo
{
const TopoDS_Shape& shape;
TopoShapeCache::Ancestry& cache;
TopAbs_ShapeEnum type;
const char* shapetype;
ShapeInfo(const TopoDS_Shape& shape, TopAbs_ShapeEnum type, TopoShapeCache::Ancestry& cache)
: shape(shape)
, cache(cache)
, type(type)
, shapetype(TopoShape::shapeName(type).c_str())
{}
int count() const
{
return cache.count();
}
TopoDS_Shape find(int index)
{
return cache.find(shape, index);
}
int find(const TopoDS_Shape& subshape)
{
return cache.find(shape, subshape);
}
};
////////////////////////////////////////
// makESHAPE -> makeShapeWithElementMap
///////////////////////////////////////
struct NameKey
{
Data::MappedName name;
long tag = 0;
int shapetype = 0;
NameKey()
= default;
explicit NameKey(Data::MappedName n)
: name(std::move(n))
{}
NameKey(int type, Data::MappedName n)
: name(std::move(n))
{
// Order the shape type from vertex < edge < face < other. We'll rely
// on this for sorting when we name the geometry element.
switch (type) {
case TopAbs_VERTEX:
shapetype = 0;
break;
case TopAbs_EDGE:
shapetype = 1;
break;
case TopAbs_FACE:
shapetype = 2;
break;
default:
shapetype = 3;
}
}
bool operator<(const NameKey& other) const
{
if (shapetype < other.shapetype) {
return true;
}
if (shapetype > other.shapetype) {
return false;
}
if (tag < other.tag) {
return true;
}
if (tag > other.tag) {
return false;
}
return name < other.name;
}
};
struct NameInfo
{
int index {};
Data::ElementIDRefs sids;
const char* shapetype {};
};
const std::string& modPostfix()
{
static std::string postfix(Data::POSTFIX_MOD);
return postfix;
}
const std::string& modgenPostfix()
{
static std::string postfix(Data::POSTFIX_MODGEN);
return postfix;
}
const std::string& genPostfix()
{
static std::string postfix(Data::POSTFIX_GEN);
return postfix;
}
const std::string& upperPostfix()
{
static std::string postfix(Data::POSTFIX_UPPER);
return postfix;
}
const std::string& lowerPostfix()
{
static std::string postfix(Data::POSTFIX_LOWER);
return postfix;
}
// TODO: Refactor checkForParallelOrCoplanar to reduce complexity
void checkForParallelOrCoplanar(const TopoDS_Shape& newShape,
const ShapeInfo& newInfo,
std::vector<TopoDS_Shape>& newShapes,
const gp_Pln& pln,
int parallelFace,
int& coplanarFace,
int& checkParallel)
{
for (TopExp_Explorer xp(newShape, newInfo.type); xp.More(); xp.Next()) {
newShapes.push_back(xp.Current());
if ((parallelFace < 0 || coplanarFace < 0) && checkParallel > 0) {
// Specialized checking for high level mapped
// face that are either coplanar or parallel
// with the source face, which are common in
// operations like extrusion. Once found, the
// first coplanar face will assign an index of
// INT_MIN+1, and the first parallel face
// INT_MIN. The purpose of these special
// indexing is to make the name more stable for
// those generated faces.
//
// For example, the top or bottom face of an
// extrusion will be named using the extruding
// face. With a fixed index, the name is no
// longer affected by adding/removing of holes
// inside the extruding face/sketch.
gp_Pln plnOther;
if (TopoShape(newShapes.back()).findPlane(plnOther)) {
if (pln.Axis().IsParallel(plnOther.Axis(), Precision::Angular())) {
if (coplanarFace < 0) {
gp_Vec vec(pln.Axis().Location(), plnOther.Axis().Location());
Standard_Real D1 = gp_Vec(pln.Axis().Direction()).Dot(vec);
if (D1 < 0) {
D1 = -D1;
}
Standard_Real D2 = gp_Vec(plnOther.Axis().Direction()).Dot(vec);
if (D2 < 0) {
D2 = -D2;
}
if (D1 <= Precision::Confusion() && D2 <= Precision::Confusion()) {
coplanarFace = (int)newShapes.size();
continue;
}
}
if (parallelFace < 0) {
parallelFace = (int)newShapes.size();
}
}
}
}
}
}
// TODO: Refactor makeShapeWithElementMap to reduce complexity
TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape,
const Mapper& mapper,
const std::vector<TopoShape>& shapes,
const char* op)
{
setShape(shape);
if (shape.IsNull()) {
FC_THROWM(NullShapeException, "Null shape");
}
if (shapes.empty()) {
return *this;
}
size_t canMap = 0;
for (auto& incomingShape : shapes) {
if (canMapElement(incomingShape)) {
++canMap;
}
}
if (canMap == 0U) {
return *this;
}
if (canMap != shapes.size() && FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("Not all input shapes are mappable"); // NOLINT
}
if (!op) {
op = Part::OpCodes::Maker;
}
std::string _op = op;
_op += '_';
initCache();
ShapeInfo vertexInfo(_Shape, TopAbs_VERTEX, _cache->getAncestry(TopAbs_VERTEX));
ShapeInfo edgeInfo(_Shape, TopAbs_EDGE, _cache->getAncestry(TopAbs_EDGE));
ShapeInfo faceInfo(_Shape, TopAbs_FACE, _cache->getAncestry(TopAbs_FACE));
mapSubElement(shapes, op);
std::array<ShapeInfo*, 3> infos = {&vertexInfo, &edgeInfo, &faceInfo};
std::array<ShapeInfo*, TopAbs_SHAPE> infoMap {};
infoMap[TopAbs_VERTEX] = &vertexInfo;
infoMap[TopAbs_EDGE] = &edgeInfo;
infoMap[TopAbs_WIRE] = &edgeInfo;
infoMap[TopAbs_FACE] = &faceInfo;
infoMap[TopAbs_SHELL] = &faceInfo;
infoMap[TopAbs_SOLID] = &faceInfo;
infoMap[TopAbs_COMPOUND] = &faceInfo;
infoMap[TopAbs_COMPSOLID] = &faceInfo;
std::ostringstream ss;
std::string postfix;
Data::MappedName newName;
std::map<Data::IndexedName, std::map<NameKey, NameInfo>> newNames;
// First, collect names from other shapes that generates or modifies the
// new shape
for (auto& pinfo : infos) {
auto& info = *pinfo;
for (const auto & incomingShape : shapes) {
if (!canMapElement(incomingShape)) {
continue;
}
auto& otherMap = incomingShape._cache->getAncestry(info.type);
if (otherMap.count() == 0) {
continue;
}
for (int i = 1; i <= otherMap.count(); i++) {
const auto& otherElement = otherMap.find(incomingShape._Shape, i);
// Find all new objects that are a modification of the old object
Data::ElementIDRefs sids;
NameKey key(info.type,
incomingShape.getMappedName(Data::IndexedName::fromConst(info.shapetype, i),
true,
&sids));
int newShapeCounter = 0;
for (auto& newShape : mapper.modified(otherElement)) {
++newShapeCounter;
if (newShape.ShapeType() >= TopAbs_SHAPE) {
// NOLINTNEXTLINE
FC_ERR("unknown modified shape type " << newShape.ShapeType() << " from "
<< info.shapetype << i);
continue;
}
auto& newInfo = *infoMap.at(newShape.ShapeType());
if (newInfo.type != newShape.ShapeType()) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
// TODO: it seems modified shape may report higher
// level shape type just like generated shape below.
// Maybe we shall do the same for name construction.
// NOLINTNEXTLINE
FC_WARN("modified shape type " << shapeName(newShape.ShapeType())
<< " mismatch with " << info.shapetype
<< i);
}
continue;
}
int newShapeIndex = newInfo.find(newShape);
if (newShapeIndex == 0) {
// This warning occurs in makERevolve. It generates
// some shape from a vertex that never made into the
// final shape. There may be incomingShape cases there.
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
// NOLINTNEXTLINE
FC_WARN("Cannot find " << op << " modified " << newInfo.shapetype
<< " from " << info.shapetype << i);
}
continue;
}
Data::IndexedName element = Data::IndexedName::fromConst(newInfo.shapetype, newShapeIndex);
if (getMappedName(element)) {
continue;
}
key.tag = incomingShape.Tag;
auto& name_info = newNames[element][key];
name_info.sids = sids;
name_info.index = newShapeCounter;
name_info.shapetype = info.shapetype;
}
int checkParallel = -1;
gp_Pln pln;
// Find all new objects that were generated from an old object
// (e.g. a face generated from an edge)
newShapeCounter = 0;
for (auto& newShape : mapper.generated(otherElement)) {
if (newShape.ShapeType() >= TopAbs_SHAPE) {
// NOLINTNEXTLINE
FC_ERR("unknown generated shape type " << newShape.ShapeType() << " from "
<< info.shapetype << i);
continue;
}
int parallelFace = -1;
int coplanarFace = -1;
auto& newInfo = *infoMap.at(newShape.ShapeType());
std::vector<TopoDS_Shape> newShapes;
int shapeOffset = 0;
if (newInfo.type == newShape.ShapeType()) {
newShapes.push_back(newShape);
}
else {
// It is possible for the maker to report generating a
// higher level shape, such as shell or solid. For
// example, when extruding, OCC will report the
// extruding face generating the entire solid. However,
// it will also report the edges of the extruding face
// generating the side faces. In this case, too much
// information is bad for us. We don't want the name of
// the side face (and its edges) to be coupled with
// incomingShape (unrelated) edges in the extruding face.
//
// shapeOffset below is used to make sure the higher
// level mapped names comes late after sorting. We'll
// ignore those names if there are more precise mapping
// available.
shapeOffset = 3;
if (info.type == TopAbs_FACE && checkParallel < 0) {
if (!TopoShape(otherElement).findPlane(pln)) {
checkParallel = 0;
}
else {
checkParallel = 1;
}
}
checkForParallelOrCoplanar(newShape,
newInfo,
newShapes,
pln,
parallelFace,
coplanarFace,
checkParallel);
}
key.shapetype += shapeOffset;
for (auto& workingShape : newShapes) {
++newShapeCounter;
int workingShapeIndex = newInfo.find(workingShape);
if (workingShapeIndex == 0) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
// NOLINTNEXTLINE
FC_WARN("Cannot find " << op << " generated " << newInfo.shapetype
<< " from " << info.shapetype << i);
}
continue;
}
Data::IndexedName element =
Data::IndexedName::fromConst(newInfo.shapetype, workingShapeIndex);
auto mapped = getMappedName(element);
if (mapped) {
continue;
}
key.tag = incomingShape.Tag;
auto& name_info = newNames[element][key];
name_info.sids = sids;
if (newShapeCounter == parallelFace) {
name_info.index = std::numeric_limits<int>::min();
}
else if (newShapeCounter == coplanarFace) {
name_info.index = std::numeric_limits<int>::min() + 1;
}
else {
name_info.index = -newShapeCounter;
}
name_info.shapetype = info.shapetype;
}
key.shapetype -= shapeOffset;
}
}
}
}
// We shall first exclude those names generated from high level mapping. If
// there are still any unnamed elements left after we go through the process
// below, we set delayed=true, and start using those excluded names.
bool delayed = false;
while (true) {
// Construct the names for modification/generation info collected in
// the previous step
for (auto itName = newNames.begin(), itNext = itName; itNext != newNames.end();
itName = itNext) {
// We treat the first modified/generated source shape name specially.
// If case there are more than one source shape. We hash the first
// source name separately, and then obtain the second string id by
// hashing all the source names together. We then use the second
// string id as the postfix for our name.
//
// In this way, we can associate the same source that are modified by
// multiple other shapes.
++itNext;
auto& element = itName->first;
auto& names = itName->second;
const auto& first_key = names.begin()->first;
auto& first_info = names.begin()->second;
if (!delayed && first_key.shapetype >= 3 && first_info.index > INT_MIN + 1) {
// This name is mapped from high level (shell, solid, etc.)
// Delay till next round.
//
// index>INT_MAX+1 is for checking generated coplanar and
// parallel face mapping, which has special fixed index to make
// name stable. These names are not delayed.
continue;
}
if (!delayed && getMappedName(element)) {
newNames.erase(itName);
continue;
}
int name_type =
first_info.index > 0 ? 1 : 2; // index>0 means modified, or else generated
Data::MappedName first_name = first_key.name;
Data::ElementIDRefs sids(first_info.sids);
postfix.clear();
if (names.size() > 1) {
ss.str("");
ss << '(';
bool first = true;
auto it = names.begin();
int count = 0;
for (++it; it != names.end(); ++it) {
auto& other_key = it->first;
if (other_key.shapetype >= 3 && first_key.shapetype < 3) {
// shapetype>=3 means it's a high level mapping (e.g. a face
// generates a solid). We don't want that if there are more
// precise low level mapping available. See comments above
// for more details.
break;
}
if (first) {
first = false;
}
else {
ss << '|';
}
auto& other_info = it->second;
std::ostringstream ss2;
if (other_info.index != 1) {
// 'K' marks the additional source shape of this
// generate (or modified) shape.
ss2 << elementMapPrefix() << 'K';
if (other_info.index == INT_MIN) {
ss2 << '0';
}
else if (other_info.index == INT_MIN + 1) {
ss2 << "00";
}
else {
// The same source shape may generate or modify
// more than one shape. The index here marks the
// position it is reported by OCC. Including the
// index here is likely to degrade name stablilty,
// but is unfortunately a necessity to avoid
// duplicate names.
ss2 << other_info.index;
}
}
Data::MappedName other_name = other_key.name;
elementMap()->encodeElementName(*other_info.shapetype,
other_name,
ss2,
&sids,
Tag,
nullptr,
other_key.tag);
ss << other_name;
if ((name_type == 1 && other_info.index < 0)
|| (name_type == 2 && other_info.index > 0)) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("element is both generated and modified"); // NOLINT
}
name_type = 0;
}
sids += other_info.sids;
// To avoid the name becoming to long, just put some limit here
if (++count == 4) {
break;
}
}
if (!first) {
ss << ')';
if (Hasher) {
sids.push_back(Hasher->getID(ss.str().c_str()));
ss.str("");
ss << sids.back().toString();
}
postfix = ss.str();
}
}
ss.str("");
if (name_type == 2) {
ss << genPostfix();
}
else if (name_type == 1) {
ss << modPostfix();
}
else {
ss << modgenPostfix();
}
if (first_info.index == INT_MIN) {
ss << '0';
}
else if (first_info.index == INT_MIN + 1) {
ss << "00";
}
else if (abs(first_info.index) > 1) {
ss << abs(first_info.index);
}
ss << postfix;
elementMap()
->encodeElementName(element[0], first_name, ss, &sids, Tag, op, first_key.tag);
elementMap()->setElementName(element, first_name, Tag, &sids);
if (!delayed && first_key.shapetype < 3) {
newNames.erase(itName);
}
}
// The reverse pass. Starting from the highest level element, i.e.
// Face, for any element that are named, assign names for its lower unnamed
// elements. For example, if Edge1 is named E1, and its vertexes are not
// named, then name them as E1;U1, E1;U2, etc.
//
// In order to make the name as stable as possible, we may assign multiple
// names (which must be sorted, because we may use the first one to name
// upper element in the final pass) to lower element if it appears in
// multiple higher elements, e.g. same edge in multiple faces.
for (size_t infoIndex = infos.size() - 1; infoIndex != 0; --infoIndex) {
std::map<Data::IndexedName,
std::map<Data::MappedName, NameInfo, Data::ElementNameComparator>>
names;
auto& info = *infos.at(infoIndex);
auto& next = *infos.at(infoIndex - 1);
int elementCounter = 1;
auto it = newNames.end();
if (delayed) {
it = newNames.upper_bound(Data::IndexedName::fromConst(info.shapetype, 0));
}
for (;; ++elementCounter) {
Data::IndexedName element;
if (!delayed) {
if (elementCounter > info.count()) {
break;
}
element = Data::IndexedName::fromConst(info.shapetype, elementCounter);
if (newNames.count(element) != 0U) {
continue;
}
}
else if (it == newNames.end()
|| !boost::starts_with(it->first.getType(), info.shapetype)) {
break;
}
else {
element = it->first;
++it;
elementCounter = element.getIndex();
if (elementCounter == 0 || elementCounter > info.count()) {
continue;
}
}
Data::ElementIDRefs sids;
Data::MappedName mapped = getMappedName(element, false, &sids);
if (!mapped) {
continue;
}
TopTools_IndexedMapOfShape submap;
TopExp::MapShapes(info.find(elementCounter), next.type, submap);
for (int submapIndex = 1, infoCounter = 1; submapIndex <= submap.Extent(); ++submapIndex) {
ss.str("");
int elementIndex = next.find(submap(submapIndex));
assert(elementIndex);
Data::IndexedName indexedName = Data::IndexedName::fromConst(next.shapetype, elementIndex);
if (getMappedName(indexedName)) {
continue;
}
auto& infoRef = names[indexedName][mapped];
infoRef.index = infoCounter++;
infoRef.sids = sids;
}
}
// Assign the actual names
for (auto& [indexedName, nameInfoMap] : names) {
// Do we really want multiple names for an element in this case?
// If not, we just pick the name in the first sorting order here.
auto& nameInfoMapEntry = *nameInfoMap.begin();
{
auto& nameInfo = nameInfoMapEntry.second;
auto& sids = nameInfo.sids;
newName = nameInfoMapEntry.first;
ss.str("");
ss << upperPostfix();
if (nameInfo.index > 1) {
ss << nameInfo.index;
}
elementMap()->encodeElementName(indexedName[0], newName, ss, &sids, Tag, op);
elementMap()->setElementName(indexedName, newName, Tag, &sids);
}
}
}
// The forward pass. For any elements that are not named, try construct its
// name from the lower elements
bool hasUnnamed = false;
for (size_t ifo = 1; ifo < infos.size(); ++ifo) {
auto& info = *infos.at(ifo);
auto& prev = *infos.at(ifo-1);
for (int i = 1; i <= info.count(); ++i) {
Data::IndexedName element = Data::IndexedName::fromConst(info.shapetype, i);
if (getMappedName(element)) {
continue;
}
Data::ElementIDRefs sids;
std::map<Data::MappedName, Data::IndexedName, Data::ElementNameComparator> names;
TopExp_Explorer xp;
if (info.type == TopAbs_FACE) {
xp.Init(BRepTools::OuterWire(TopoDS::Face(info.find(i))), TopAbs_EDGE);
}
else {
xp.Init(info.find(i), prev.type);
}
for (; xp.More(); xp.Next()) {
int previousElementIndex = prev.find(xp.Current());
assert(previousElementIndex);
Data::IndexedName prevElement = Data::IndexedName::fromConst(prev.shapetype, previousElementIndex);
if (!delayed && (newNames.count(prevElement) != 0U)) {
names.clear();
break;
}
Data::ElementIDRefs sid;
Data::MappedName name = getMappedName(prevElement, false, &sid);
if (!name) {
// only assign name if all lower elements are named
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("unnamed lower element " << prevElement); // NOLINT
}
names.clear();
break;
}
auto res = names.emplace(name, prevElement);
if (res.second) {
sids += sid;
}
else if (prevElement != res.first->second) {
// The seam edge will appear twice, which is normal. We
// only warn if the mapped element names are different.
// NOLINTNEXTLINE
FC_WARN("lower element " << prevElement << " and " << res.first->second
<< " has duplicated name " << name << " for "
<< info.shapetype << i);
}
}
if (names.empty()) {
hasUnnamed = true;
continue;
}
auto it = names.begin();
newName = it->first;
if (names.size() == 1) {
ss << lowerPostfix();
}
else {
bool first = true;
ss.str("");
if (!Hasher) {
ss << lowerPostfix();
}
ss << '(';
int count = 0;
for (++it; it != names.end(); ++it) {
if (first) {
first = false;
}
else {
ss << '|';
}
ss << it->first;
// To avoid the name becoming to long, just put some limit here
if (++count == 4) {
break;
}
}
ss << ')';
if (Hasher) {
sids.push_back(Hasher->getID(ss.str().c_str()));
ss.str("");
ss << lowerPostfix() << sids.back().toString();
}
}
elementMap()->encodeElementName(element[0], newName, ss, &sids, Tag, op);
elementMap()->setElementName(element, newName, Tag, &sids);
}
}
if (!hasUnnamed || delayed || newNames.empty()) {
break;
}
delayed = true;
}
return *this;
}
namespace
{
void addShapesToBuilder(const std::vector<TopoShape>& shapes,
@@ -768,7 +1514,8 @@ TopoShape& TopoShape::makeElementFace(const std::vector<TopoShape>& shapes,
/**
* Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name.
*
* @param element The element name(type) that provides 1 one character suffix to the name IF <conditions>.
* @param element The element name(type) that provides 1 one character suffix to the name IF
* <conditions>.
* @param names The subnames to build the name from. If empty, return the TopoShape MappedName.
* @param marker The elementMap name or suffix to start the name with. If null, use the
* elementMapPrefix.
@@ -1015,6 +1762,29 @@ struct MapperSewing: Part::TopoShape::Mapper
return _res;
}
};
struct MapperFill: Part::TopoShape::Mapper
{
BRepFill_Generator& maker;
explicit MapperFill(BRepFill_Generator& maker)
: maker(maker)
{}
const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override
{
_res.clear();
try {
TopTools_ListIteratorOfListOfShape it;
for (it.Initialize(maker.GeneratedShapes(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>& MapperMaker::modified(const TopoDS_Shape& s) const
{
@@ -1050,4 +1820,117 @@ const std::vector<TopoDS_Shape>& MapperMaker::generated(const TopoDS_Shape& s) c
return _res;
}
// topo naming counterpart of TopoShape::makeShell()
TopoShape& TopoShape::makeElementShell(bool silent, const char* op)
{
if (silent) {
if (isNull()) {
return *this;
}
if (shapeType(true) != TopAbs_COMPOUND) {
return *this;
}
// we need a compound that consists of only faces
TopExp_Explorer it;
// no shells
if (hasSubShape(TopAbs_SHELL)) {
return *this;
}
// no wires outside a face
it.Init(_Shape, TopAbs_WIRE, TopAbs_FACE);
if (it.More()) {
return *this;
}
// no edges outside a wire
it.Init(_Shape, TopAbs_EDGE, TopAbs_WIRE);
if (it.More()) {
return *this;
}
// no vertexes outside an edge
it.Init(_Shape, TopAbs_VERTEX, TopAbs_EDGE);
if (it.More()) {
return *this;
}
}
else if (!hasSubShape(TopAbs_FACE)) {
FC_THROWM(Base::CADKernelError, "Cannot make shell without face");
}
BRep_Builder builder;
TopoDS_Shape shape;
TopoDS_Shell shell;
builder.MakeShell(shell);
try {
for (const auto& face : getSubShapes(TopAbs_FACE)) {
builder.Add(shell, face);
}
TopoShape tmp(Tag, Hasher, shell);
tmp.resetElementMap();
tmp.mapSubElement(*this, op);
shape = shell;
BRepCheck_Analyzer check(shell);
if (!check.IsValid()) {
ShapeUpgrade_ShellSewing sewShell;
shape = sewShell.ApplySewing(shell);
// TODO confirm the above won't change OCCT topological naming
}
if (shape.IsNull()) {
if (silent) {
return *this;
}
FC_THROWM(NullShapeException, "Failed to make shell");
}
if (shape.ShapeType() != TopAbs_SHELL) {
if (silent) {
return *this;
}
FC_THROWM(Base::CADKernelError,
"Failed to make shell: unexpected output shape type "
<< shapeType(shape.ShapeType(), true));
}
setShape(shape);
resetElementMap(tmp.elementMap());
}
catch (Standard_Failure& e) {
if (!silent) {
FC_THROWM(Base::CADKernelError, "Failed to make shell: " << e.GetMessageString());
}
}
return *this;
}
// TopoShape& TopoShape::makeElementShellFromWires(const std::vector<TopoShape>& wires,
// bool silent,
// const char* op)
// {
// BRepFill_Generator maker;
// for (auto& w : wires) {
// if (w.shapeType(silent) == TopAbs_WIRE) {
// maker.AddWire(TopoDS::Wire(w.getShape()));
// }
// }
// if (wires.empty()) {
// if (silent) {
// _Shape.Nullify();
// return *this;
// }
// FC_THROWM(NullShapeException, "No input shapes");
// }
// maker.Perform();
// this->makeShapeWithElementMap(maker.Shell(), MapperFill(maker), wires, op);
// return *this;
// }
} // namespace Part