Part/Toponaming: Add original code for makeShapeWithElementMap
Called TopoShape::makESHAPE in the original Toponaming branch. The code here has been modified as little as possible to get it to compile in current main.
This commit is contained in:
@@ -638,6 +638,44 @@ public:
|
||||
void mapSubElement(const std::vector<TopoShape> &shapes, const char *op=nullptr);
|
||||
bool hasPendingElementMap() const;
|
||||
|
||||
/** Helper class to return the generated and modified shape given an input shape
|
||||
*
|
||||
* Shape history information is extracted using OCCT APIs
|
||||
* BRepBuilderAPI_MakeShape::Generated/Modified(). However, there is often
|
||||
* some glitches in various derived class. So we use this class as an
|
||||
* abstraction, and create various derived classes to deal with the glitches.
|
||||
*/
|
||||
struct PartExport Mapper {
|
||||
/// Helper vector for temporary storage of both generated and modified shapes
|
||||
mutable std::vector<TopoDS_Shape> _res;
|
||||
virtual ~Mapper() {}
|
||||
/// Return a list of shape generated from the given input shape
|
||||
virtual const std::vector<TopoDS_Shape> &generated(const TopoDS_Shape &) const {
|
||||
return _res;
|
||||
}
|
||||
/// Return a list of shape modified from the given input shape
|
||||
virtual const std::vector<TopoDS_Shape> &modified(const TopoDS_Shape &) const {
|
||||
return _res;
|
||||
}
|
||||
};
|
||||
|
||||
/** Core function to generate mapped element names from shape history
|
||||
*
|
||||
* @param shape: the new shape
|
||||
* @param mapper: for mapping input shapes to generated/modified shapes
|
||||
* @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 given 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 &makeShapeWithElementMap(const TopoDS_Shape &shape,
|
||||
const Mapper &mapper,
|
||||
const std::vector<TopoShape> &sources,
|
||||
const char *op=nullptr);
|
||||
|
||||
/** Helper class to return the generated and modified shape given an input shape
|
||||
*
|
||||
|
||||
@@ -25,21 +25,23 @@
|
||||
|
||||
#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>
|
||||
|
||||
#endif
|
||||
|
||||
#include <BRepCheck_Analyzer.hxx>
|
||||
#include <BRepFill_Generator.hxx>
|
||||
#include <ShapeUpgrade_ShellSewing.hxx>
|
||||
#include "TopoShape.h"
|
||||
#include "TopoShapeCache.h"
|
||||
#include "TopoShapeOpCode.h"
|
||||
#include "FaceMaker.h"
|
||||
|
||||
|
||||
@@ -338,6 +340,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,
|
||||
@@ -387,7 +391,8 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other,
|
||||
}
|
||||
std::ostringstream ss;
|
||||
char elementType {shapeName(type)[0]};
|
||||
if ( ! elementMap() ) {
|
||||
if (!elementMap()) {
|
||||
// NOLINTNEXTLINE
|
||||
FC_THROWM(NullShapeException, "No element map");
|
||||
}
|
||||
elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag);
|
||||
@@ -509,6 +514,735 @@ 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(const Data::MappedName& n)
|
||||
: name(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(TopoShape::elementMapPrefix() + ":M");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string& modgenPostfix()
|
||||
{
|
||||
static std::string postfix(modPostfix() + "G");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string& genPostfix()
|
||||
{
|
||||
static std::string postfix(TopoShape::elementMapPrefix() + ":G");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string& upperPostfix()
|
||||
{
|
||||
static std::string postfix(TopoShape::elementMapPrefix() + ":U");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
const std::string& lowerPostfix()
|
||||
{
|
||||
static std::string postfix(TopoShape::elementMapPrefix() + ":L");
|
||||
return postfix;
|
||||
}
|
||||
|
||||
// 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 vinfo(_Shape, TopAbs_VERTEX, _cache->getAncestry(TopAbs_VERTEX));
|
||||
ShapeInfo einfo(_Shape, TopAbs_EDGE, _cache->getAncestry(TopAbs_EDGE));
|
||||
ShapeInfo finfo(_Shape, TopAbs_FACE, _cache->getAncestry(TopAbs_FACE));
|
||||
mapSubElement(shapes, op);
|
||||
|
||||
std::array<ShapeInfo*, 3> infos = {&vinfo, &einfo, &finfo};
|
||||
|
||||
std::array<ShapeInfo*, TopAbs_SHAPE> infoMap{};
|
||||
infoMap[TopAbs_VERTEX] = &vinfo;
|
||||
infoMap[TopAbs_EDGE] = &einfo;
|
||||
infoMap[TopAbs_WIRE] = &einfo;
|
||||
infoMap[TopAbs_FACE] = &finfo;
|
||||
infoMap[TopAbs_SHELL] = &finfo;
|
||||
infoMap[TopAbs_SOLID] = &finfo;
|
||||
infoMap[TopAbs_COMPOUND] = &finfo;
|
||||
infoMap[TopAbs_COMPSOLID] = &finfo;
|
||||
|
||||
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[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[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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
key.shapetype += shapeOffset;
|
||||
for (auto& workingShape : newShapes) {
|
||||
++newShapeCounter;
|
||||
int workingShapeIndex = newInfo.find(workingShape);
|
||||
if (!workingShapeIndex) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
|
||||
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[0],
|
||||
other_name,
|
||||
ss2,
|
||||
&sids,
|
||||
Tag,
|
||||
0,
|
||||
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)) {
|
||||
// NOLINTNEXTLINE
|
||||
FC_WARN("element is both generated and modified");
|
||||
}
|
||||
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[infoIndex];
|
||||
auto& next = *infos[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& actualName : names) {
|
||||
#ifndef FC_ELEMENT_MAP_ALL
|
||||
// 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& name = *actualName.second.begin();
|
||||
#else
|
||||
for (auto& name : actualName.second)
|
||||
#endif
|
||||
{
|
||||
auto& infoRef = name.second;
|
||||
auto& sids = infoRef.sids;
|
||||
newName = name.first;
|
||||
ss.str("");
|
||||
ss << upperPostfix();
|
||||
if (infoRef.index > 1) {
|
||||
ss << infoRef.index;
|
||||
}
|
||||
elementMap()->encodeElementName(actualName.first[0], newName, ss, &sids, Tag, op);
|
||||
elementMap()->setElementName(actualName.first, 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[ifo];
|
||||
auto& prev = *infos[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 j = prev.find(xp.Current());
|
||||
assert(j);
|
||||
Data::IndexedName prevElement = Data::IndexedName::fromConst(prev.shapetype, j);
|
||||
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)) {
|
||||
// NOLINTNEXTLINE
|
||||
FC_WARN("unnamed lower element " << prevElement);
|
||||
}
|
||||
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,
|
||||
@@ -634,7 +1368,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.
|
||||
|
||||
Reference in New Issue
Block a user