From d768706ebe14df2a4b5ee30198124f4855f8bdd8 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 19 Jan 2024 10:32:28 -0600 Subject: [PATCH 1/8] 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. --- src/Mod/Part/App/TopoShape.h | 38 ++ src/Mod/Part/App/TopoShapeExpansion.cpp | 753 +++++++++++++++++++++++- 2 files changed, 782 insertions(+), 9 deletions(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 573e4eb322..bf30436a82 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -638,6 +638,44 @@ public: void mapSubElement(const std::vector &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 _res; + virtual ~Mapper() {} + /// Return a list of shape generated from the given input shape + virtual const std::vector &generated(const TopoDS_Shape &) const { + return _res; + } + /// Return a list of shape modified from the given input shape + virtual const std::vector &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 &sources, + const char *op=nullptr); /** Helper class to return the generated and modified shape given an input shape * diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index e7554ebaa8..be31389811 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -25,21 +25,23 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include #endif -#include -#include -#include #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& 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& 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 infos = {&vinfo, &einfo, &finfo}; + + std::array 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> 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 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::min(); + } + else if (newShapeCounter == coplanarFace) { + name_info.index = std::numeric_limits::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> + 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 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& shapes, @@ -634,7 +1368,8 @@ TopoShape& TopoShape::makeElementFace(const std::vector& 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 . + * @param element The element name(type) that provides 1 one character suffix to the name IF + * . * @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. From d43fe277a7cc7a471bb7d6346eb97112d1666fd9 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 19 Jan 2024 14:11:07 -0700 Subject: [PATCH 2/8] Part/Toponaming: Basic linter cleanup of makeShapeWithElementMap No major refactoring. --- src/Mod/Part/App/TopoShapeExpansion.cpp | 202 ++++++++++++------------ 1 file changed, 105 insertions(+), 97 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index be31389811..a57780f7da 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -37,6 +37,7 @@ #include #include +#include #endif #include "TopoShape.h" @@ -392,8 +393,7 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other, std::ostringstream ss; char elementType {shapeName(type)[0]}; if (!elementMap()) { - // NOLINTNEXTLINE - FC_THROWM(NullShapeException, "No element map"); + FC_THROWM(NullShapeException, "No element map"); // NOLINT } elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); elementMap()->setElementName(element, name, Tag, &sids); @@ -556,8 +556,8 @@ struct NameKey NameKey() = default; - explicit NameKey(const Data::MappedName& n) - : name(n) + explicit NameKey(Data::MappedName n) + : name(std::move(n)) {} NameKey(int type, Data::MappedName n) : name(std::move(n)) @@ -598,9 +598,9 @@ struct NameKey struct NameInfo { - int index{}; + int index {}; Data::ElementIDRefs sids; - const char* shapetype{}; + const char* shapetype {}; }; @@ -634,6 +634,61 @@ const std::string& lowerPostfix() return postfix; } +// TODO: Refactor checkForParallelOrCoplanar to reduce complexity +void checkForParallelOrCoplanar(const TopoDS_Shape& newShape, + const ShapeInfo& newInfo, + std::vector& 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, @@ -669,22 +724,22 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, _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)); + 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 infos = {&vinfo, &einfo, &finfo}; + std::array infos = {&vertexInfo, &edgeInfo, &faceInfo}; - std::array 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::array 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; @@ -723,7 +778,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, << info.shapetype << i); continue; } - auto& newInfo = *infoMap[newShape.ShapeType()]; + 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 @@ -777,7 +832,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, int parallelFace = -1; int coplanarFace = -1; - auto& newInfo = *infoMap[newShape.ShapeType()]; + auto& newInfo = *infoMap.at(newShape.ShapeType()); std::vector newShapes; int shapeOffset = 0; if (newInfo.type == newShape.ShapeType()) { @@ -808,62 +863,21 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, 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(); - } - } - } - } - } + checkForParallelOrCoplanar(newShape, + newInfo, + newShapes, + pln, + parallelFace, + coplanarFace, + checkParallel); } key.shapetype += shapeOffset; for (auto& workingShape : newShapes) { ++newShapeCounter; int workingShapeIndex = newInfo.find(workingShape); - if (!workingShapeIndex) { + if (workingShapeIndex == 0) { if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { + // NOLINTNEXTLINE FC_WARN("Cannot find " << op << " generated " << newInfo.shapetype << " from " << info.shapetype << i); } @@ -989,19 +1003,18 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, } } Data::MappedName other_name = other_key.name; - elementMap()->encodeElementName(other_info.shapetype[0], + elementMap()->encodeElementName(*other_info.shapetype, other_name, ss2, &sids, Tag, - 0, + 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)) { - // NOLINTNEXTLINE - FC_WARN("element is both generated and modified"); + FC_WARN("element is both generated and modified"); // NOLINT } name_type = 0; } @@ -1065,8 +1078,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, std::map> names; - auto& info = *infos[infoIndex]; - auto& next = *infos[infoIndex - 1]; + auto& info = *infos.at(infoIndex); + auto& next = *infos.at(infoIndex - 1); int elementCounter = 1; auto it = newNames.end(); if (delayed) { @@ -1117,25 +1130,21 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, } } // Assign the actual names - for (auto& actualName : names) { -#ifndef FC_ELEMENT_MAP_ALL + 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& name = *actualName.second.begin(); -#else - for (auto& name : actualName.second) -#endif + auto& nameInfoMapEntry = *nameInfoMap.begin(); { - auto& infoRef = name.second; - auto& sids = infoRef.sids; - newName = name.first; + auto& nameInfo = nameInfoMapEntry.second; + auto& sids = nameInfo.sids; + newName = nameInfoMapEntry.first; ss.str(""); ss << upperPostfix(); - if (infoRef.index > 1) { - ss << infoRef.index; + if (nameInfo.index > 1) { + ss << nameInfo.index; } - elementMap()->encodeElementName(actualName.first[0], newName, ss, &sids, Tag, op); - elementMap()->setElementName(actualName.first, newName, Tag, &sids); + elementMap()->encodeElementName(indexedName[0], newName, ss, &sids, Tag, op); + elementMap()->setElementName(indexedName, newName, Tag, &sids); } } } @@ -1144,8 +1153,8 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, // 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]; + 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)) { @@ -1162,9 +1171,9 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, 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); + 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; @@ -1174,8 +1183,7 @@ TopoShape& TopoShape::makeShapeWithElementMap(const TopoDS_Shape& shape, 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); + FC_WARN("unnamed lower element " << prevElement); // NOLINT } names.clear(); break; From f91d39bac3a3005e083792abd5b3e46a470ae774 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 19 Jan 2024 14:55:45 -0700 Subject: [PATCH 3/8] Tests/Toponaming: Add test framework for makeShapeWithElementMap --- tests/src/Mod/Part/App/CMakeLists.txt | 2 + tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 23 +------ .../Part/App/TopoShapeExpansionHelpers.cpp | 26 ++++++++ .../Mod/Part/App/TopoShapeExpansionHelpers.h | 13 ++++ .../App/TopoShapeMakeShapeWithElementMap.cpp | 61 +++++++++++++++++++ 5 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp create mode 100644 tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h create mode 100644 tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index 9c642e65e2..4c5bedf30d 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -15,5 +15,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/TopoShape.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansionHelpers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMakeShapeWithElementMap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp ) diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index ff976589d6..dd72b48d20 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2,6 +2,7 @@ #include "gtest/gtest.h" #include "src/App/InitApplication.h" +#include "TopoShapeExpansionHelpers.h" #include #include @@ -125,30 +126,10 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesGeneratesMap) EXPECT_EQ(4, topoShape.getMappedChildElements().size()); // two vertices and two edges } -namespace -{ - -std::pair CreateTwoCubes() -{ - auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); - boxMaker1.Build(); - auto box1 = boxMaker1.Shape(); - - auto boxMaker2 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); - boxMaker2.Build(); - auto box2 = boxMaker2.Shape(); - auto transform = gp_Trsf(); - transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); - box2.Location(TopLoc_Location(transform)); - - return {box1, box2}; -} -} // namespace - TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) { // Arrange - auto [cube1, cube2] = CreateTwoCubes(); + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); Part::TopoShape cube1TS {cube1}; cube1TS.Tag = 1; Part::TopoShape cube2TS {cube2}; diff --git a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp new file mode 100644 index 0000000000..ad401ee7b6 --- /dev/null +++ b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "TopoShapeExpansionHelpers.h" + +#include + +namespace TopoShapeExpansionHelpers +{ + +std::pair CreateTwoCubes() +{ + auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); + boxMaker1.Build(); + auto box1 = boxMaker1.Shape(); + + auto boxMaker2 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); + boxMaker2.Build(); + auto box2 = boxMaker2.Shape(); + auto transform = gp_Trsf(); + transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); + box2.Location(TopLoc_Location(transform)); + + return {box1, box2}; +} + +} // namespace TopoShapeExpansionHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h new file mode 100644 index 0000000000..d92c18f4d4 --- /dev/null +++ b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef FREECAD_TOPOSHAPEEXPANSIONHELPERS_H +#define FREECAD_TOPOSHAPEEXPANSIONHELPERS_H + +#include + +namespace TopoShapeExpansionHelpers +{ +std::pair CreateTwoCubes(); +} + +#endif // FREECAD_TOPOSHAPEEXPANSIONHELPERS_H diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp new file mode 100644 index 0000000000..f135e23303 --- /dev/null +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +// Tests for the makeShapeWithElementMap method, extracted from the main set of tests for TopoShape +// due to length and complexity. + +#include "gtest/gtest.h" +#include "src/App/InitApplication.h" +#include "TopoShapeExpansionHelpers.h" +#include + +#include + +class TopoShapeMakeShapeWithElementMapTests: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + App::GetApplication().newDocument(_docName.c_str(), "testUser"); + _sids = &_sid; + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + + Part::TopoShape* Shape() + { + return &_shape; + } + + Part::TopoShape::Mapper* Mapper() + { + return &_mapper; + } + +private: + std::string _docName; + Data::ElementIDRefs _sid; + QVector* _sids = nullptr; + Part::TopoShape _shape; + Part::TopoShape::Mapper _mapper; +}; + +TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + TopoDS_Vertex nullShape; + + // Act and assert + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullShape, *Mapper(), sources), + Part::NullShapeException); +} From 43f4db445a9ebb585db52cd6c70a3f6949432236 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Wed, 24 Jan 2024 17:07:11 -0500 Subject: [PATCH 4/8] Element Map Test --- .../App/TopoShapeMakeShapeWithElementMap.cpp | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index f135e23303..584652d5c5 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -7,7 +7,7 @@ #include "src/App/InitApplication.h" #include "TopoShapeExpansionHelpers.h" #include - +// #include #include class TopoShapeMakeShapeWithElementMapTests: public ::testing::Test @@ -48,6 +48,7 @@ private: Part::TopoShape::Mapper _mapper; }; + TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) { // Arrange @@ -59,3 +60,47 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) EXPECT_THROW(Shape()->makeShapeWithElementMap(nullShape, *Mapper(), sources), Part::NullShapeException); } + +using Data::IndexedName, Data::MappedName; +using Part::TopoShape; +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapVertex) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapEdge) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapWire) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapFace) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapShell) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapSolid) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompound) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompSolid) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapOffsetCubes) +TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto transform {gp_Trsf()}; + transform.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); + cube2.Move(TopLoc_Location(transform)); + std::vector sources {cube1, cube2}; + sources[0].Tag = 1; + sources[1].Tag = 2; + // sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); + // sources[1].makeShapeWithElementMap(sources[1].getShape(), *Mapper(), {sources[1]}); + + TopoShape compound; + compound.makeElementCompound(sources); + // Act + compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); + auto elements = compound.getElementMap(); + std::map mapped; + for (auto name : elements) { + mapped.emplace(name.index, name.name); + } + // Assert + EXPECT_EQ(elements.size(), 52); + EXPECT_EQ(mapped[IndexedName("Edge1")], MappedName("Edge1;MAK;:H1:4,E")); + EXPECT_EQ(mapped[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); + // EXPECT_STREQ(mapped[IndexedName("Edge1")],MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); + // EXPECT_STREQ(mapped[IndexedName("Edge",13)],MappedName("Edge1;MAK;:H:4,E;MAK;:H2:f,E")); + EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); + EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); + EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); +} From 5135564fb4d683ddd746ed92a71471bbf18fa014 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Wed, 24 Jan 2024 20:10:55 -0500 Subject: [PATCH 5/8] Rename test and add more assertions --- .../App/TopoShapeMakeShapeWithElementMap.cpp | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index 584652d5c5..cc1bcf0916 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -63,16 +63,15 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) using Data::IndexedName, Data::MappedName; using Part::TopoShape; + // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapVertex) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapEdge) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapWire) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapFace) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapShell) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapSolid) -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompound) -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompSolid) -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapOffsetCubes) -TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) + +TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompound) { // Arrange auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); @@ -82,25 +81,40 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) std::vector sources {cube1, cube2}; sources[0].Tag = 1; sources[1].Tag = 2; - // sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); - // sources[1].makeShapeWithElementMap(sources[1].getShape(), *Mapper(), {sources[1]}); - + // Map only one of the two sources to test different names. + sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); TopoShape compound; compound.makeElementCompound(sources); + auto preElements = compound.getElementMap(); // Map before mapping. + std::map preMap; + for (auto name : preElements) { + preMap.emplace(name.index, name.name); + } // Act compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); - auto elements = compound.getElementMap(); - std::map mapped; + auto elements = compound.getElementMap(); // Map after mapping + std::map map; for (auto name : elements) { - mapped.emplace(name.index, name.name); + map.emplace(name.index, name.name); } // Assert - EXPECT_EQ(elements.size(), 52); - EXPECT_EQ(mapped[IndexedName("Edge1")], MappedName("Edge1;MAK;:H1:4,E")); - EXPECT_EQ(mapped[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); - // EXPECT_STREQ(mapped[IndexedName("Edge1")],MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); - // EXPECT_STREQ(mapped[IndexedName("Edge",13)],MappedName("Edge1;MAK;:H:4,E;MAK;:H2:f,E")); + EXPECT_EQ(preElements.size(), 52); // Check the before map. + EXPECT_EQ(preMap[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;:H1:b,E")); + EXPECT_EQ(preMap[IndexedName("Edge", 13)], MappedName("Edge1;:H2,E")); + + EXPECT_EQ(elements.size(), 52); // 12 Edges, 8 Vertexes, 6 Faces per each Cube + EXPECT_EQ(map[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); + EXPECT_EQ(map[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); + EXPECT_EQ(map.count(IndexedName("Edge", 24)), 1); + EXPECT_EQ(map.count(IndexedName("Edge", 25)), 0); + EXPECT_EQ(map.count(IndexedName("Vertex", 16)), 1); + EXPECT_EQ(map.count(IndexedName("Vertex", 17)), 0); + EXPECT_EQ(map.count(IndexedName("Face", 12)), 1); + EXPECT_EQ(map.count(IndexedName("Face", 13)), 0); EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); } +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompSolid) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapOffsetCubes) +// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) From 2e9891226603c3a839c31b170d5ec30a7c92e61e Mon Sep 17 00:00:00 2001 From: Vincenzo Calligaro Date: Sat, 27 Jan 2024 13:36:38 +0100 Subject: [PATCH 6/8] Tests for makeShapeWithElementMap (#12) * Tests for makeShapeWithElementMap * Added nullShapesThrows tests for the classes derived from * Added test passing as "sources" parameter of the tested method an empty vector of TopoShapes objects * Added test with unmappable sources * Added test to find shapes inside other shapes made from the first ones * Tests for makeShapeWithElementMap with simple shapes * Added test to find source shapes' sub-shapes name in the elements maps * Added test to find the "MAK" OpCode in the elements maps names --------- Signed-off-by: CalligaroV --- .../App/TopoShapeMakeShapeWithElementMap.cpp | 190 +++++++++++++++++- 1 file changed, 188 insertions(+), 2 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index cc1bcf0916..99548567be 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -7,8 +7,20 @@ #include "src/App/InitApplication.h" #include "TopoShapeExpansionHelpers.h" #include +#include // #include + #include +#include +#include +#include +#include +#include +#include +#include + +using namespace Part; +using namespace Data; class TopoShapeMakeShapeWithElementMapTests: public ::testing::Test { @@ -54,10 +66,31 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) // Arrange auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; - TopoDS_Vertex nullShape; + TopoDS_Vertex nullVertex; + TopoDS_Edge nullEdge; + TopoDS_Wire nullWire; + TopoDS_Face nullFace; + TopoDS_Shell nullShell; + TopoDS_Solid nullSolid; + TopoDS_CompSolid nullCompSolid; + TopoDS_Compound nullCompound; // Act and assert - EXPECT_THROW(Shape()->makeShapeWithElementMap(nullShape, *Mapper(), sources), + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullVertex, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullEdge, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullWire, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullFace, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullShell, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullSolid, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullCompSolid, *Mapper(), sources), + Part::NullShapeException); + EXPECT_THROW(Shape()->makeShapeWithElementMap(nullCompound, *Mapper(), sources), Part::NullShapeException); } @@ -118,3 +151,156 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompound) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompSolid) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapOffsetCubes) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) + +TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector emptySources; + std::vector nonEmptySources {cube1, cube2}; + + // Act and assert + for (auto& source : nonEmptySources) { + Part::TopoShape& modifiedShape = source; + Part::TopoShape& originalShape = source; + + EXPECT_EQ( + &originalShape, + &modifiedShape.makeShapeWithElementMap(source.getShape(), *Mapper(), emptySources)); + } +} + +TEST_F(TopoShapeMakeShapeWithElementMapTests, nonMappableSources) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + + // Act and assert + for (auto& source : sources) { + size_t canMap = 0; + for (const auto& mappableSource : sources) { + if (source.canMapElement(mappableSource)) { + ++canMap; + } + } + + if (canMap == 0U) { + EXPECT_EQ(&source, + &source.makeShapeWithElementMap(source.getShape(), *Mapper(), sources)); + } + } +} + +TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceShapesInShape) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + sources[1].Tag = 2; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + Part::TopoShape cmpdShape; + cmpdShape.makeElementCompound(sources); + + // Act and assert + for (const auto& source : sources) { + std::vector tmpSources {source}; + for (const auto& subSource : sources) { + Part::TopoShape tmpShape {source.getShape()}; + tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), tmpSources); + if (&source == &subSource) { + EXPECT_NE(tmpShape.findShape(subSource.getShape()), + 0); // if tmpShape uses, for example, cube1 and we search for cube1 than + // we should find it + } + else { + EXPECT_EQ(tmpShape.findShape(subSource.getShape()), + 0); // if tmpShape uses, for example, cube1 and we search for cube2 than + // we shouldn't find it + } + } + EXPECT_NE(cmpdShape.findShape(source.getShape()), + 0); // as cmpdShape is made with cube1 and cube2 we should find both of them + } +} + +TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceSubShapesInElementMap) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + sources[1].Tag = 2; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + + // Act and assert + // Testing with all the source TopoShapes + for (const auto& source : sources) { + TopoShape tmpShape {source.getShape()}; + tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); + + // First we create a map with the IndexedNames and MappedNames + std::map elementStdMap; + for (const auto& mappedElement : tmpShape.getElementMap()) { + elementStdMap.emplace(mappedElement.index, mappedElement.name); + } + + // Then for all the elements types (Vertex, Edge, Face) ... + for (const auto& shapeType : source.getElementTypes()) { + std::string shapeTypeStr {shapeType}; + + // ... and all the elements of the various types in the source TopoShape ... + for (unsigned long shapeIndex = 1U; shapeIndex <= source.countSubElements(shapeType); + shapeIndex++) { + std::string shapeIndexStr = std::to_string(shapeIndex); + std::string shapeName {shapeTypeStr + shapeIndexStr}; + + IndexedName indexedName {shapeTypeStr.c_str(), (int)shapeIndex}; + MappedName mappedName {elementStdMap[indexedName]}; + const char shapeTypePrefix[1] {indexedName.toString()[0]}; + + EXPECT_NO_THROW(elementStdMap.at(indexedName)); // .. we check that the IndexedName + // is one of the keys... + EXPECT_NE(mappedName.find(shapeName.c_str()), + -1); // ... that the element name is in the MappedName... + EXPECT_EQ( + mappedName.rfind(shapeTypePrefix), + mappedName.toString().length() + - 1); // ... that the element prefix is at the end of the MappedName ... + } + + // ... we also check that we don't find shapes that don't exist and therefor that don't + // have neither an IndexedName nor a MappedName + IndexedName fakeIndexedName {shapeTypeStr.c_str(), + (int)source.countSubElements(shapeType) + 1}; + EXPECT_THROW(elementStdMap.at(fakeIndexedName), std::out_of_range); + } + } +} + +TEST_F(TopoShapeMakeShapeWithElementMapTests, findMakerOpInElementMap) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + sources[1].Tag = 2; // setting Tag explicitly otherwise it is likely that this test will be + // more or less the same of nonMappableSources + + // Act and assert + // Testing with all the source TopoShapes + for (const auto& source : sources) { + TopoShape tmpShape {source.getShape()}; + tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); + + // For all the mappedElements ... + for (const auto& mappedElement : tmpShape.getElementMap()) { + EXPECT_NE(mappedElement.name.find(OpCodes::Maker), + -1); // ... we check that there's the "MAK" OpCode + } + } +} From 88b5191b5ef9b36b988b960a8ba4fe29a2e17443 Mon Sep 17 00:00:00 2001 From: bgbsww <120601209+bgbsww@users.noreply.github.com> Date: Sat, 27 Jan 2024 09:43:39 -0500 Subject: [PATCH 7/8] Cleaned up complex test (#15) * Element Map Test * Rename test and add more assertions * Reduce test complexity --- .../App/TopoShapeMakeShapeWithElementMap.cpp | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index 99548567be..7ee0f759ca 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -60,7 +60,6 @@ private: Part::TopoShape::Mapper _mapper; }; - TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) { // Arrange @@ -97,6 +96,16 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) using Data::IndexedName, Data::MappedName; using Part::TopoShape; +std::map elementMap(const TopoShape &shape) +{ + std::map result {}; + auto elements = shape.getElementMap(); + for (auto const & entry : elements) { + result[entry.index] = entry.name; + } + return result; +} + // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapVertex) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapEdge) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapWire) @@ -104,53 +113,54 @@ using Part::TopoShape; // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapShell) // TEST_F(TopoShapeMakeShapeWithElementMapTests, mapSolid) -TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompound) +TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) +{ + // Arrange + auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + std::vector sources {cube1, cube2}; + sources[0].Tag = 1; + sources[1].Tag = 2; + TopoShape compound = TopoShape(); + compound.makeElementCompound(sources); + auto preElements = elementMap(compound); // Map before mapping. + // Act + compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); + auto postElements = elementMap(compound); // Map after mapping + // Assert + EXPECT_EQ(preElements.size(), 52); // Check the before map. + EXPECT_EQ(postElements.size(), 52); // 12 Edges, 8 Vertexes, 6 Faces per each Cube + EXPECT_EQ(postElements.count(IndexedName("Edge", 24)), 1); + EXPECT_EQ(postElements.count(IndexedName("Edge", 25)), 0); + EXPECT_EQ(postElements.count(IndexedName("Vertex", 16)), 1); + EXPECT_EQ(postElements.count(IndexedName("Vertex", 17)), 0); + EXPECT_EQ(postElements.count(IndexedName("Face", 12)), 1); + EXPECT_EQ(postElements.count(IndexedName("Face", 13)), 0); + EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); + EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); + EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); +} + +TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundMap) { // Arrange auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); - auto transform {gp_Trsf()}; - transform.SetTranslation(gp_Vec(gp_XYZ(-0.5, -0.5, 0))); - cube2.Move(TopLoc_Location(transform)); std::vector sources {cube1, cube2}; sources[0].Tag = 1; sources[1].Tag = 2; // Map only one of the two sources to test different names. sources[0].makeShapeWithElementMap(sources[0].getShape(), *Mapper(), {sources[0]}); - TopoShape compound; + TopoShape compound = TopoShape(); compound.makeElementCompound(sources); - auto preElements = compound.getElementMap(); // Map before mapping. - std::map preMap; - for (auto name : preElements) { - preMap.emplace(name.index, name.name); - } + auto preElements = elementMap(compound); // Map before mapping. // Act compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); - auto elements = compound.getElementMap(); // Map after mapping - std::map map; - for (auto name : elements) { - map.emplace(name.index, name.name); - } + auto postElements = elementMap(compound); // Map after mapping // Assert - EXPECT_EQ(preElements.size(), 52); // Check the before map. - EXPECT_EQ(preMap[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;:H1:b,E")); - EXPECT_EQ(preMap[IndexedName("Edge", 13)], MappedName("Edge1;:H2,E")); - - EXPECT_EQ(elements.size(), 52); // 12 Edges, 8 Vertexes, 6 Faces per each Cube - EXPECT_EQ(map[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); - EXPECT_EQ(map[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); - EXPECT_EQ(map.count(IndexedName("Edge", 24)), 1); - EXPECT_EQ(map.count(IndexedName("Edge", 25)), 0); - EXPECT_EQ(map.count(IndexedName("Vertex", 16)), 1); - EXPECT_EQ(map.count(IndexedName("Vertex", 17)), 0); - EXPECT_EQ(map.count(IndexedName("Face", 12)), 1); - EXPECT_EQ(map.count(IndexedName("Face", 13)), 0); - EXPECT_STREQ(sources[0].shapeName().c_str(), "Solid"); - EXPECT_STREQ(sources[1].shapeName().c_str(), "Solid"); - EXPECT_STREQ(compound.shapeName().c_str(), "Compound"); + EXPECT_EQ(preElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;:H1:b,E")); + EXPECT_EQ(preElements[IndexedName("Edge", 13)], MappedName("Edge1;:H2,E")); + EXPECT_EQ(postElements[IndexedName("Edge", 1)], MappedName("Edge1;MAK;:H:4,E;MAK;:H1:f,E")); + EXPECT_EQ(postElements[IndexedName("Edge", 13)], MappedName("Edge1;MAK;:H2:4,E")); } -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompSolid) -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapOffsetCubes) -// TEST_F(TopoShapeMakeShapeWithElementMapTests, mapIntersectingCubes) TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) { From e24ed3486732f2fa0829aa8f7f23d7e98ccb1e1f Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 27 Jan 2024 10:49:59 -0600 Subject: [PATCH 8/8] Tests/Toponaming: Post-merge cleanup --- src/Mod/Part/App/TopoShape.h | 21 --- src/Mod/Part/App/TopoShapeExpansion.cpp | 12 +- tests/src/Mod/Part/App/CMakeLists.txt | 1 - tests/src/Mod/Part/App/PartTestHelpers.cpp | 18 ++ tests/src/Mod/Part/App/PartTestHelpers.h | 2 + tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 140 +++++++-------- .../Part/App/TopoShapeExpansionHelpers.cpp | 26 --- .../Mod/Part/App/TopoShapeExpansionHelpers.h | 13 -- .../App/TopoShapeMakeShapeWithElementMap.cpp | 166 ++++++++++-------- 9 files changed, 190 insertions(+), 209 deletions(-) delete mode 100644 tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp delete mode 100644 tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index bf30436a82..b04caf52ed 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -677,27 +677,6 @@ public: const std::vector &sources, const char *op=nullptr); - /** 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 _res; - virtual ~Mapper() {} - /// Return a list of shape generated from the given input shape - virtual const std::vector &generated(const TopoDS_Shape &) const { - return _res; - } - /// Return a list of shape modified from the given input shape - virtual const std::vector &modified(const TopoDS_Shape &) const { - return _res; - } - }; - /** Make a compound shape * * @param shapes: input shapes diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index a57780f7da..eb6b03be29 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -45,6 +45,8 @@ #include "TopoShapeOpCode.h" #include "FaceMaker.h" +#include + FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -606,31 +608,31 @@ struct NameInfo const std::string& modPostfix() { - static std::string postfix(TopoShape::elementMapPrefix() + ":M"); + static std::string postfix(Data::POSTFIX_MOD); return postfix; } const std::string& modgenPostfix() { - static std::string postfix(modPostfix() + "G"); + static std::string postfix(Data::POSTFIX_MODGEN); return postfix; } const std::string& genPostfix() { - static std::string postfix(TopoShape::elementMapPrefix() + ":G"); + static std::string postfix(Data::POSTFIX_GEN); return postfix; } const std::string& upperPostfix() { - static std::string postfix(TopoShape::elementMapPrefix() + ":U"); + static std::string postfix(Data::POSTFIX_UPPER); return postfix; } const std::string& lowerPostfix() { - static std::string postfix(TopoShape::elementMapPrefix() + ":L"); + static std::string postfix(Data::POSTFIX_LOWER); return postfix; } diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index 4c5bedf30d..f239b3a27b 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -15,7 +15,6 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/TopoShape.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansion.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansionHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMakeShapeWithElementMap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp ) diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 8b0390b7e5..36dc63f067 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later +#include + #include "PartTestHelpers.h" // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) @@ -137,6 +139,22 @@ std::map elementMap(const TopoShape& shape) return result; } +std::pair CreateTwoCubes() +{ + auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); + boxMaker1.Build(); + auto box1 = boxMaker1.Shape(); + + auto boxMaker2 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); + boxMaker2.Build(); + auto box2 = boxMaker2.Shape(); + auto transform = gp_Trsf(); + transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); + box2.Location(TopLoc_Location(transform)); + + return {box1, box2}; +} + } // namespace PartTestHelpers // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) diff --git a/tests/src/Mod/Part/App/PartTestHelpers.h b/tests/src/Mod/Part/App/PartTestHelpers.h index 79147f6531..8b829e4ba6 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.h +++ b/tests/src/Mod/Part/App/PartTestHelpers.h @@ -57,4 +57,6 @@ boxesMatch(const Base::BoundBox3d& b1, const Base::BoundBox3d& b2, double prec = std::map elementMap(const TopoShape& shape); +std::pair CreateTwoCubes(); + } // namespace PartTestHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index dd72b48d20..f1b82a9822 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -2,23 +2,21 @@ #include "gtest/gtest.h" #include "src/App/InitApplication.h" -#include "TopoShapeExpansionHelpers.h" #include #include #include "PartTestHelpers.h" #include -#include #include #include #include #include -#include #include // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) +using namespace PartTestHelpers; class TopoShapeExpansionTest: public ::testing::Test { @@ -32,7 +30,6 @@ protected: { _docName = App::GetApplication().getUniqueDocumentName("test"); App::GetApplication().newDocument(_docName.c_str(), "testUser"); - _sids = &_sid; _hasher = Base::Reference(new App::StringHasher); ASSERT_EQ(_hasher.getRefCount(), 1); } @@ -46,7 +43,6 @@ protected: private: std::string _docName; Data::ElementIDRefs _sid; - QVector* _sids = nullptr; App::StringHasherRef _hasher; }; @@ -129,7 +125,7 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoShapesGeneratesMap) TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = CreateTwoCubes(); Part::TopoShape cube1TS {cube1}; cube1TS.Tag = 1; Part::TopoShape cube2TS {cube2}; @@ -150,42 +146,20 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) // 26 subshapes each } -std::tuple -CreateRectFace(float len = 2.0, float wid = 3.0) -{ - auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(len, 0.0, 0.0)).Edge(); - auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, 0.0, 0.0), gp_Pnt(len, wid, 0.0)).Edge(); - auto edge3 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, wid, 0.0), gp_Pnt(0.0, wid, 0.0)).Edge(); - auto edge4 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, wid, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge(); - auto wire1 = BRepBuilderAPI_MakeWire({edge1, edge2, edge3, edge4}).Wire(); - auto face1 = BRepBuilderAPI_MakeFace(wire1).Face(); - return {face1, wire1, edge1, edge2, edge3, edge4}; -} - -std::tuple -CreateFaceWithRoundHole(float len = 2.0, float wid = 3.0, float radius = 1.0) -{ - auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(len, wid); - auto circ1 = - GC_MakeCircle(gp_Pnt(len / 2.0, wid / 2.0, 0), gp_Dir(0.0, 0.0, 1.0), radius).Value(); - auto edge5 = BRepBuilderAPI_MakeEdge(circ1).Edge(); - auto wire2 = BRepBuilderAPI_MakeWire(edge5).Wire(); - auto face2 = BRepBuilderAPI_MakeFace(face1, wire2).Face(); - return {face2, wire1, wire2}; -} - TEST_F(TopoShapeExpansionTest, makeElementFaceNull) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(nullptr); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_FALSE(face1.IsEqual(newFace.getShape())); EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); @@ -198,15 +172,17 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceNull) TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(wire1); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); @@ -220,16 +196,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) TEST_F(TopoShapeExpansionTest, makeElementFaceParams) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); @@ -243,16 +221,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceParams) TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); @@ -267,15 +247,17 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {wire1, 1L}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", nullptr, nullptr); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); @@ -290,16 +272,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {wire2, 1L}; - double area = PartTestHelpers::getArea(face1); - double area1 = PartTestHelpers::getArea(topoShape.getShape()); + double area = getArea(face1); + double area1 = getArea(topoShape.getShape()); // Act Part::TopoShape newFace = topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr); - double area2 = PartTestHelpers::getArea(newFace.getShape()); - double area3 = PartTestHelpers::getArea(topoShape.getShape()); + double area2 = getArea(newFace.getShape()); + double area3 = getArea(topoShape.getShape()); // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); @@ -395,7 +379,9 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) TEST_F(TopoShapeExpansionTest, splitWires) { // Arrange - const double Len = 3, Wid = 2, Rad = 1; + const float Len = 3; + const float Wid = 2; + const float Rad = 1; auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; std::vector inner; @@ -405,8 +391,8 @@ TEST_F(TopoShapeExpansionTest, splitWires) topoShape.splitWires(&inner, Part::TopoShape::SplitWireReorient::ReorientReversed); // Assert EXPECT_EQ(inner.size(), 1); - EXPECT_FLOAT_EQ(PartTestHelpers::getLength(wire.getShape()), 2 + 2 + 3 + 3); - EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * Rad * 2); + EXPECT_FLOAT_EQ(getLength(wire.getShape()), 2 + 2 + 3 + 3); + EXPECT_FLOAT_EQ(getLength(inner.front().getShape()), M_PI * Rad * 2); EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); for (Part::TopoShape& shape : inner) { EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD); @@ -415,8 +401,8 @@ TEST_F(TopoShapeExpansionTest, splitWires) // Possible future tests: // splitWires without inner Wires -// splitWires with allfour reorientation values NoReorient, ReOrient, ReorientForward, -// ReorientRevesed +// splitWires with all four reorientation values NoReorient, ReOrient, ReorientForward, +// ReorientReversed TEST_F(TopoShapeExpansionTest, mapSubElementInvalidParm) { @@ -442,7 +428,8 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindShapeByNames) Part::TopoShape cube2TS {cube2}; cube1TS.Tag = 1; cube2TS.Tag = 2; - Part::TopoShape topoShape, topoShape1; + Part::TopoShape topoShape; + Part::TopoShape topoShape1; // Act int fs1 = topoShape1.findShape(cube1); @@ -527,8 +514,13 @@ TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors) cube2TS.Tag = 2; cube3TS.Tag = 3; cube4TS.Tag = 4; - Part::TopoShape topoShape, topoShape1, topoShape2; - Part::TopoShape topoShape3, topoShape4, topoShape5, topoShape6; + Part::TopoShape topoShape; + Part::TopoShape topoShape1; + Part::TopoShape topoShape2; + Part::TopoShape topoShape3; + Part::TopoShape topoShape4; + Part::TopoShape topoShape5; + Part::TopoShape topoShape6; topoShape.makeElementCompound({cube1TS, cube2TS}); topoShape1.makeElementCompound({cube3TS, cube4TS}); topoShape2.makeElementCompound({cube1TS, cube3TS}); @@ -576,7 +568,8 @@ TEST_F(TopoShapeExpansionTest, makeElementShellInvalid) TEST_F(TopoShapeExpansionTest, makeElementShellSingle) { // Arrange - const double Len = 3, Wid = 2; + const float Len = 3; + const float Wid = 2; auto [face1, wire1, edge1, edge2, edge3, _] = CreateRectFace(Len, Wid); Part::TopoShape topoShape {face1, 1L}; // Act @@ -594,7 +587,8 @@ TEST_F(TopoShapeExpansionTest, makeElementShellSingle) TEST_F(TopoShapeExpansionTest, makeElementShellOpen) { // Arrange - const double Len = 3, Wid = 2; + const float Len = 3; + const float Wid = 2; auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(Len, Wid); auto transform {gp_Trsf()}; transform.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), M_PI / 2); @@ -625,7 +619,7 @@ TEST_F(TopoShapeExpansionTest, makeElementShellClosed) Part::TopoShape topoShape {cube1}; std::vector shapes; for (const auto& face : topoShape.getSubShapes(TopAbs_FACE)) { - shapes.push_back(Part::TopoShape {face}); + shapes.emplace_back(face); } // Act Part::TopoShape topoShape1 {1L}; @@ -652,11 +646,11 @@ TEST_F(TopoShapeExpansionTest, makeElementShellIntersecting) Part::TopoShape topoShape {cube1}; std::vector shapes; for (const auto& face : topoShape.getSubShapes(TopAbs_FACE)) { - shapes.push_back(Part::TopoShape {face}); + shapes.emplace_back(face); } topoShape.setShape(cube2); for (const auto& face : topoShape.getSubShapes(TopAbs_FACE)) { - shapes.push_back(Part::TopoShape {face}); + shapes.emplace_back(face); } // Act Part::TopoShape topoShape1 {1L}; diff --git a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp deleted file mode 100644 index ad401ee7b6..0000000000 --- a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "TopoShapeExpansionHelpers.h" - -#include - -namespace TopoShapeExpansionHelpers -{ - -std::pair CreateTwoCubes() -{ - auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); - boxMaker1.Build(); - auto box1 = boxMaker1.Shape(); - - auto boxMaker2 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); - boxMaker2.Build(); - auto box2 = boxMaker2.Shape(); - auto transform = gp_Trsf(); - transform.SetTranslation(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)); - box2.Location(TopLoc_Location(transform)); - - return {box1, box2}; -} - -} // namespace TopoShapeExpansionHelpers diff --git a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h b/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h deleted file mode 100644 index d92c18f4d4..0000000000 --- a/tests/src/Mod/Part/App/TopoShapeExpansionHelpers.h +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later - -#ifndef FREECAD_TOPOSHAPEEXPANSIONHELPERS_H -#define FREECAD_TOPOSHAPEEXPANSIONHELPERS_H - -#include - -namespace TopoShapeExpansionHelpers -{ -std::pair CreateTwoCubes(); -} - -#endif // FREECAD_TOPOSHAPEEXPANSIONHELPERS_H diff --git a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp index 7ee0f759ca..d7827ff781 100644 --- a/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMakeShapeWithElementMap.cpp @@ -5,7 +5,7 @@ #include "gtest/gtest.h" #include "src/App/InitApplication.h" -#include "TopoShapeExpansionHelpers.h" +#include "PartTestHelpers.h" #include #include // #include @@ -34,7 +34,6 @@ protected: { _docName = App::GetApplication().getUniqueDocumentName("test"); App::GetApplication().newDocument(_docName.c_str(), "testUser"); - _sids = &_sid; } void TearDown() override @@ -52,10 +51,12 @@ protected: return &_mapper; } + void testFindSourceSubShapesInElementMapForSource(const std::vector& sources, + const TopoShape& source); + private: std::string _docName; Data::ElementIDRefs _sid; - QVector* _sids = nullptr; Part::TopoShape _shape; Part::TopoShape::Mapper _mapper; }; @@ -63,7 +64,7 @@ private: TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; TopoDS_Vertex nullVertex; TopoDS_Edge nullEdge; @@ -96,11 +97,11 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nullShapeThrows) using Data::IndexedName, Data::MappedName; using Part::TopoShape; -std::map elementMap(const TopoShape &shape) +std::map elementMap(const TopoShape& shape) { std::map result {}; auto elements = shape.getElementMap(); - for (auto const & entry : elements) { + for (auto const& entry : elements) { result[entry.index] = entry.name; } return result; @@ -116,7 +117,7 @@ std::map elementMap(const TopoShape &shape) TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; sources[0].Tag = 1; sources[1].Tag = 2; @@ -127,7 +128,7 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) compound.makeShapeWithElementMap(compound.getShape(), *Mapper(), sources); auto postElements = elementMap(compound); // Map after mapping // Assert - EXPECT_EQ(preElements.size(), 52); // Check the before map. + EXPECT_EQ(preElements.size(), 52); // Check the before map. EXPECT_EQ(postElements.size(), 52); // 12 Edges, 8 Vertexes, 6 Faces per each Cube EXPECT_EQ(postElements.count(IndexedName("Edge", 24)), 1); EXPECT_EQ(postElements.count(IndexedName("Edge", 25)), 0); @@ -143,7 +144,7 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundCount) TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundMap) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; sources[0].Tag = 1; sources[1].Tag = 2; @@ -165,7 +166,7 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, mapCompoundMap) TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector emptySources; std::vector nonEmptySources {cube1, cube2}; @@ -183,7 +184,7 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, emptySourceShapes) TEST_F(TopoShapeMakeShapeWithElementMapTests, nonMappableSources) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; // Act and assert @@ -202,10 +203,34 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, nonMappableSources) } } +void testFindSourceShapesInSingleShape(const Part::TopoShape& cmpdShape, + const Part::TopoShape& source, + const std::vector& sources, + const TopoShape::Mapper& mapper) +{ + std::vector tmpSources {source}; + for (const auto& subSource : sources) { + Part::TopoShape tmpShape {source.getShape()}; + tmpShape.makeShapeWithElementMap(source.getShape(), mapper, tmpSources); + if (&source == &subSource) { + EXPECT_NE(tmpShape.findShape(subSource.getShape()), + 0); // if tmpShape uses, for example, cube1 and we search for cube1 than + // we should find it + } + else { + EXPECT_EQ(tmpShape.findShape(subSource.getShape()), + 0); // if tmpShape uses, for example, cube1 and we search for cube2 than + // we shouldn't find it + } + } + EXPECT_NE(cmpdShape.findShape(source.getShape()), + 0); // as cmpdShape is made with cube1 and cube2 we should find both of them +} + TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceShapesInShape) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be // more or less the same of nonMappableSources @@ -216,30 +241,69 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceShapesInShape) // Act and assert for (const auto& source : sources) { - std::vector tmpSources {source}; - for (const auto& subSource : sources) { - Part::TopoShape tmpShape {source.getShape()}; - tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), tmpSources); - if (&source == &subSource) { - EXPECT_NE(tmpShape.findShape(subSource.getShape()), - 0); // if tmpShape uses, for example, cube1 and we search for cube1 than - // we should find it - } - else { - EXPECT_EQ(tmpShape.findShape(subSource.getShape()), - 0); // if tmpShape uses, for example, cube1 and we search for cube2 than - // we shouldn't find it - } - } - EXPECT_NE(cmpdShape.findShape(source.getShape()), - 0); // as cmpdShape is made with cube1 and cube2 we should find both of them + testFindSourceShapesInSingleShape(cmpdShape, source, sources, *Mapper()); + } +} + +void testFindSubShapesForSourceWithTypeAndIndex(const std::string& shapeTypeStr, + std::map& elementStdMap, + unsigned long shapeIndex) +{ + std::string shapeIndexStr = std::to_string(shapeIndex); + std::string shapeName {shapeTypeStr + shapeIndexStr}; + + IndexedName indexedName {shapeTypeStr.c_str(), (int)shapeIndex}; + MappedName mappedName {elementStdMap[indexedName]}; + const char shapeTypePrefix {indexedName.toString()[0]}; + + EXPECT_NO_THROW(elementStdMap.at(indexedName)); // We check that the IndexedName + // is one of the keys... + EXPECT_NE(mappedName.find(shapeName.c_str()), + -1); // ... that the element name is in the MappedName... + EXPECT_EQ(mappedName.toString().back(), shapeTypePrefix); +} + +void testFindSubShapesForSourceWithType(const TopoShape& source, + const char* shapeType, + std::map& elementStdMap) +{ + std::string shapeTypeStr {shapeType}; + + // ... and all the elements of the various types in the source TopoShape ... + for (unsigned long shapeIndex = 1U; shapeIndex <= source.countSubElements(shapeType); + shapeIndex++) { + testFindSubShapesForSourceWithTypeAndIndex(shapeTypeStr, elementStdMap, shapeIndex); + } + + // ... we also check that we don't find shapes that don't exist and therefore don't + // have either an IndexedName or a MappedName + IndexedName fakeIndexedName {shapeTypeStr.c_str(), (int)source.countSubElements(shapeType) + 1}; + EXPECT_THROW(elementStdMap.at(fakeIndexedName), std::out_of_range); +} + +void TopoShapeMakeShapeWithElementMapTests::testFindSourceSubShapesInElementMapForSource( + const std::vector& sources, + const TopoShape& source) +{ + TopoShape tmpShape {source.getShape()}; + tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); + + // First we create a map with the IndexedNames and MappedNames + std::map elementStdMap; + for (const auto& mappedElement : tmpShape.getElementMap()) { + elementStdMap.emplace(mappedElement.index, mappedElement.name); + } + + // Then for all the elements types (Vertex, Edge, Face) ... + for (const auto& shapeType : source.getElementTypes()) { + testFindSubShapesForSourceWithType(source, shapeType, elementStdMap); } } TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceSubShapesInElementMap) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be // more or less the same of nonMappableSources @@ -249,52 +313,14 @@ TEST_F(TopoShapeMakeShapeWithElementMapTests, findSourceSubShapesInElementMap) // Act and assert // Testing with all the source TopoShapes for (const auto& source : sources) { - TopoShape tmpShape {source.getShape()}; - tmpShape.makeShapeWithElementMap(source.getShape(), *Mapper(), sources); - - // First we create a map with the IndexedNames and MappedNames - std::map elementStdMap; - for (const auto& mappedElement : tmpShape.getElementMap()) { - elementStdMap.emplace(mappedElement.index, mappedElement.name); - } - - // Then for all the elements types (Vertex, Edge, Face) ... - for (const auto& shapeType : source.getElementTypes()) { - std::string shapeTypeStr {shapeType}; - - // ... and all the elements of the various types in the source TopoShape ... - for (unsigned long shapeIndex = 1U; shapeIndex <= source.countSubElements(shapeType); - shapeIndex++) { - std::string shapeIndexStr = std::to_string(shapeIndex); - std::string shapeName {shapeTypeStr + shapeIndexStr}; - - IndexedName indexedName {shapeTypeStr.c_str(), (int)shapeIndex}; - MappedName mappedName {elementStdMap[indexedName]}; - const char shapeTypePrefix[1] {indexedName.toString()[0]}; - - EXPECT_NO_THROW(elementStdMap.at(indexedName)); // .. we check that the IndexedName - // is one of the keys... - EXPECT_NE(mappedName.find(shapeName.c_str()), - -1); // ... that the element name is in the MappedName... - EXPECT_EQ( - mappedName.rfind(shapeTypePrefix), - mappedName.toString().length() - - 1); // ... that the element prefix is at the end of the MappedName ... - } - - // ... we also check that we don't find shapes that don't exist and therefor that don't - // have neither an IndexedName nor a MappedName - IndexedName fakeIndexedName {shapeTypeStr.c_str(), - (int)source.countSubElements(shapeType) + 1}; - EXPECT_THROW(elementStdMap.at(fakeIndexedName), std::out_of_range); - } + testFindSourceSubShapesInElementMapForSource(sources, source); } } TEST_F(TopoShapeMakeShapeWithElementMapTests, findMakerOpInElementMap) { // Arrange - auto [cube1, cube2] = TopoShapeExpansionHelpers::CreateTwoCubes(); + auto [cube1, cube2] = PartTestHelpers::CreateTwoCubes(); std::vector sources {cube1, cube2}; sources[0].Tag = 1; // setting Tag explicitly otherwise it is likely that this test will be // more or less the same of nonMappableSources