// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * * * Copyright (c) 2022 Zheng, Lei * * Copyright (c) 2023 FreeCAD Project Association * * * * This file is part of FreeCAD. * * * * FreeCAD is free software: you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 2.1 of the * * License, or (at your option) any later version. * * * * FreeCAD is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with FreeCAD. If not, see * * . * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #endif #include "TopoShape.h" #include "TopoShapeCache.h" FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT namespace Part { void TopoShape::initCache(int reset) const { if (reset > 0 || !_cache || _cache->isTouched(_Shape)) { if (_parentCache) { _parentCache.reset(); _subLocation.Identity(); } _cache = std::make_shared(_Shape); } } void TopoShape::setShape(const TopoDS_Shape& shape, bool resetElementMap) { if (resetElementMap) { this->resetElementMap(); } else if (_cache && _cache->isTouched(shape)) { this->flushElementMap(); } //_Shape._Shape = shape; // TODO: Replace the next line with this once ShapeProtector is // available. _Shape = shape; if (_cache) { initCache(); } } TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const TopLoc_Location& location) { #if OCC_VERSION_HEX < 0x070600 tds.Move(location); #else tds.Move(location, false); #endif return tds; } TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const TopLoc_Location& location) { #if OCC_VERSION_HEX < 0x070600 return tds.Moved(location); #else return tds.Moved(location, false); #endif } TopoDS_Shape& TopoShape::move(TopoDS_Shape& tds, const gp_Trsf& transfer) { #if OCC_VERSION_HEX < 0x070600 static constexpr double scalePrecision {1e-14}; if (std::abs(transfer.ScaleFactor()) > scalePrecision) #else if (std::abs(transfer.ScaleFactor()) > TopLoc_Location::ScalePrec()) #endif { auto transferCopy(transfer); transferCopy.SetScaleFactor(1.0); tds.Move(transferCopy); } else { tds.Move(transfer); } return tds; } TopoDS_Shape TopoShape::moved(const TopoDS_Shape& tds, const gp_Trsf& transfer) { TopoDS_Shape sCopy(tds); return move(sCopy, transfer); } TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const TopLoc_Location& loc) { tds.Location(TopLoc_Location()); return move(tds, loc); } TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const TopLoc_Location& loc) { auto sCopy(tds); sCopy.Location(TopLoc_Location()); return moved(sCopy, loc); } TopoDS_Shape& TopoShape::locate(TopoDS_Shape& tds, const gp_Trsf& transfer) { tds.Location(TopLoc_Location()); return move(tds, transfer); } TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer) { auto sCopy(tds); sCopy.Location(TopLoc_Location()); return moved(sCopy, transfer); } int TopoShape::findShape(const TopoDS_Shape& subshape) const { initCache(); return _cache->findShape(_Shape, subshape); } TopoDS_Shape TopoShape::findShape(const char* name) const { if (!name) { return {}; } Data::MappedElement res = getElementName(name); if (!res.index) { return {}; } auto idx = shapeTypeAndIndex(name); if (idx.second == 0) { return {}; } initCache(); return _cache->findShape(_Shape, idx.first, idx.second); } TopoDS_Shape TopoShape::findShape(TopAbs_ShapeEnum type, int idx) const { initCache(); return _cache->findShape(_Shape, type, idx); } int TopoShape::findAncestor(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const { initCache(); return _cache->findShape(_Shape, _cache->findAncestor(_Shape, subshape, type)); } TopoDS_Shape TopoShape::findAncestorShape(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const { initCache(); return _cache->findAncestor(_Shape, subshape, type); } std::vector TopoShape::findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const { const auto& shapes = findAncestorsShapes(subshape, type); std::vector ret; ret.reserve(shapes.size()); for (const auto& shape : shapes) { ret.push_back(findShape(shape)); } return ret; } std::vector TopoShape::findAncestorsShapes(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const { initCache(); std::vector shapes; _cache->findAncestor(_Shape, subshape, type, &shapes); return shapes; } // The following lines should be used for now to replace the original macros (in the future we can // refactor to use std::source_location and eliminate the use of the macros entirely). // FC_THROWM(NullShapeException, "Null shape"); // FC_THROWM(NullShapeException, "Null input shape"); // FC_WARN("Null input shape"); // NOLINT // // The original macros: // #define HANDLE_NULL_SHAPE _HANDLE_NULL_SHAPE("Null shape",true) // #define HANDLE_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",true) // #define WARN_NULL_INPUT _HANDLE_NULL_SHAPE("Null input shape",false) bool TopoShape::hasPendingElementMap() const { return !elementMap(false) && this->_cache && (this->_parentCache || this->_cache->cachedElementMap); } bool TopoShape::canMapElement(const TopoShape& other) const { if (isNull() || other.isNull() || this == &other || other.Tag == -1 || Tag == -1) { return false; } if ((other.Tag == 0) && !other.elementMap(false) && !other.hasPendingElementMap()) { return false; } initCache(); other.initCache(); _cache->relations.clear(); return true; } namespace { size_t checkSubshapeCount(const TopoShape& topoShape1, const TopoShape& topoShape2, TopAbs_ShapeEnum elementType) { auto count = topoShape1.countSubShapes(elementType); auto other = topoShape2.countSubShapes(elementType); if (count != other) { FC_WARN("sub shape mismatch"); // NOLINT if (count > other) { count = other; } } return count; } } // namespace void TopoShape::setupChild(Data::ElementMap::MappedChildElements& child, TopAbs_ShapeEnum elementType, const TopoShape& topoShape, size_t shapeCount, const char* op) { child.indexedName = Data::IndexedName::fromConst(TopoShape::shapeName(elementType).c_str(), 1); child.offset = 0; child.count = static_cast(shapeCount); child.elementMap = topoShape.elementMap(); if (this->Tag != topoShape.Tag) { child.tag = topoShape.Tag; } else { child.tag = 0; } if (op) { child.postfix = op; } } void TopoShape::copyElementMap(const TopoShape& topoShape, const char* op) { if (topoShape.isNull() || isNull()) { return; } std::vector children; std::array elementTypes = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; for (const auto elementType : elementTypes) { auto count = checkSubshapeCount(*this, topoShape, elementType); if (count == 0) { continue; } children.emplace_back(); auto& child = children.back(); setupChild(child, elementType, topoShape, count, op); } resetElementMap(); if (!Hasher) { Hasher = topoShape.Hasher; } setMappedChildElements(children); } namespace { void warnIfLogging() { if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) { FC_WARN("hasher mismatch"); // NOLINT } }; void hasherMismatchError() { FC_ERR("hasher mismatch"); // NOLINT } void checkAndMatchHasher(TopoShape& topoShape1, const TopoShape& topoShape2) { if (topoShape1.Hasher) { if (topoShape2.Hasher != topoShape1.Hasher) { if (topoShape1.getElementMapSize(false) == 0U) { warnIfLogging(); } else { hasherMismatchError(); } topoShape1.Hasher = topoShape2.Hasher; } } else { topoShape1.Hasher = topoShape2.Hasher; } } } // namespace void TopoShape::mapSubElementTypeForShape(const TopoShape& other, TopAbs_ShapeEnum type, const char* op, int count, bool forward, bool& warned) { auto& shapeMap = _cache->getAncestry(type); auto& otherMap = other._cache->getAncestry(type); const char* shapeType = shapeName(type).c_str(); // 1-indexed for readability (e.g. there is no "Edge0", we started at "Edge1", etc.) for (int outerCounter = 1; outerCounter <= count; ++outerCounter) { int innerCounter {0}; int index {0}; if (forward) { innerCounter = outerCounter; index = shapeMap.find(_Shape, otherMap.find(other._Shape, outerCounter)); if (index == 0) { continue; } } else { index = outerCounter; innerCounter = otherMap.find(other._Shape, shapeMap.find(_Shape, outerCounter)); if (innerCounter == 0) { continue; } } Data::IndexedName element = Data::IndexedName::fromConst(shapeType, index); for (auto& mappedName : other.getElementMappedNames(Data::IndexedName::fromConst(shapeType, innerCounter), true)) { auto& name = mappedName.first; auto& sids = mappedName.second; if (!sids.empty()) { if (!Hasher) { Hasher = sids[0].getHasher(); } else if (!sids[0].isFromSameHasher(Hasher)) { if (!warned) { warned = true; FC_WARN("hasher mismatch"); // NOLINT } sids.clear(); } } std::ostringstream ss; char elementType {shapeName(type)[0]}; if ( ! elementMap() ) { FC_THROWM(NullShapeException, "No element map"); } elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); elementMap()->setElementName(element, name, Tag, &sids); } } } void TopoShape::mapSubElementForShape(const TopoShape& other, const char* op) { bool warned = false; static const std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; for (auto type : types) { auto& shapeMap = _cache->getAncestry(type); auto& otherMap = other._cache->getAncestry(type); if ((shapeMap.count() == 0) || (otherMap.count() == 0)) { continue; } bool forward {false}; int count {0}; if (otherMap.count() <= shapeMap.count()) { forward = true; count = otherMap.count(); } else { forward = false; count = shapeMap.count(); } mapSubElementTypeForShape(other, type, op, count, forward, warned); } } void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool forceHasher) { if (!canMapElement(other)) { return; } if ((getElementMapSize(false) == 0U) && this->_Shape.IsPartner(other._Shape)) { if (!this->Hasher) { this->Hasher = other.Hasher; } copyElementMap(other, op); return; } if (!forceHasher && other.Hasher) { checkAndMatchHasher(*this, other); } mapSubElementForShape(other, op); } std::vector TopoShape::createChildMap(size_t count, const std::vector& shapes, const char* op) { std::vector children; children.reserve(count * (size_t)3); std::array types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE}; for (const auto topAbsType : types) { size_t offset = 0; for (auto& topoShape : shapes) { if (topoShape.isNull()) { continue; } auto subShapeCount = topoShape.countSubShapes(topAbsType); if (subShapeCount == 0) { continue; } children.emplace_back(); auto& child = children.back(); child.indexedName = Data::IndexedName::fromConst(TopoShape::shapeName(topAbsType).c_str(), 1); child.offset = static_cast(offset); offset += subShapeCount; child.count = static_cast(subShapeCount); child.elementMap = topoShape.elementMap(); child.tag = topoShape.Tag; if (op) { child.postfix = op; } } } return children; } void TopoShape::mapCompoundSubElements(const std::vector& shapes, const char* op) { int count = 0; for (auto& topoShape : shapes) { if (topoShape.isNull()) { continue; } ++count; auto subshape = getSubShape(TopAbs_SHAPE, count, /*silent = */ true); if (!subshape.IsPartner(topoShape._Shape)) { return; // Not a partner shape, don't do any mapping at all } } auto children {createChildMap(count, shapes, op)}; setMappedChildElements(children); } void TopoShape::mapSubElement(const std::vector& shapes, const char* op) { if (shapes.empty()) { return; } if (shapeType(true) == TopAbs_COMPOUND) { mapCompoundSubElements(shapes, op); } else { for (auto& shape : shapes) { mapSubElement(shape, op); } } } namespace { void addShapesToBuilder(const std::vector& shapes, BRep_Builder& builder, TopoDS_Compound& comp) { int count = 0; for (auto& topoShape : shapes) { if (topoShape.isNull()) { FC_WARN("Null input shape"); // NOLINT continue; } builder.Add(comp, topoShape.getShape()); ++count; } if (count == 0) { FC_THROWM(NullShapeException, "Null shape"); } } } // namespace TopoShape& TopoShape::makeElementCompound(const std::vector& shapes, const char* op, bool force) { if (!force && shapes.size() == 1) { *this = shapes[0]; return *this; } BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); if (shapes.empty()) { setShape(comp); return *this; } addShapesToBuilder(shapes, builder, comp); setShape(comp); initCache(); mapSubElement(shapes, op); return *this; } } // namespace Part