From 4077c55fae3444205ead9a77b90ab28af2abd08b Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 18 Nov 2023 11:28:59 -0600 Subject: [PATCH] Part/Toponaming: Add TopoShapeCache base implementation Copied from RealThunder's TopoNaming branch and modified only as much as required to enable compilation. --- src/Mod/Part/App/CMakeLists.txt | 3 + src/Mod/Part/App/TopoShape.h | 67 +++++- src/Mod/Part/App/TopoShapeCache.cpp | 25 ++ src/Mod/Part/App/TopoShapeCache.h | 292 ++++++++++++++++++++++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 43 ++++ 5 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 src/Mod/Part/App/TopoShapeCache.cpp create mode 100644 src/Mod/Part/App/TopoShapeCache.h create mode 100644 src/Mod/Part/App/TopoShapeExpansion.cpp diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 5b8721764b..bf0ce716eb 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -530,6 +530,9 @@ SET(Part_SRCS ProgressIndicator.h TopoShape.cpp TopoShape.h + TopoShapeCache.cpp + TopoShapeCache.h + TopoShapeExpansion.cpp TopoShapeOpCode.h edgecluster.cpp edgecluster.h diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index aa36ac3d95..f530a9bac7 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -47,6 +47,8 @@ class Color; namespace Part { +class TopoShapeCache; + /* A special sub-class to indicate null shapes */ class PartExport NullShapeException : public Base::ValueError @@ -99,8 +101,10 @@ public: TopoShape(const TopoShape&); ~TopoShape() override; - inline void setShape(const TopoDS_Shape& shape) { - this->_Shape = shape; + void setShape(const TopoDS_Shape& shape, bool resetElementMap=true); + + inline void setShape(const TopoShape& shape) { + *this = shape; } inline const TopoDS_Shape& getShape() const { @@ -364,12 +368,29 @@ public: void move(const TopLoc_Location &loc) { _Shape.Move(loc); } + /** Return a new shape that is moved to a new location + * + * @param loc: location + * + * @return Return a shallow copy of the shape moved to the new location + * that is applied in addition to any current transformation of the + * shape + */ TopoShape moved(const TopLoc_Location &loc) const { TopoShape ret(*this); ret._Shape.Move(loc); return ret; } + static TopoDS_Shape &move(TopoDS_Shape &s, const TopLoc_Location &); + static TopoDS_Shape moved(const TopoDS_Shape &s, const TopLoc_Location &); + static TopoDS_Shape &move(TopoDS_Shape &s, const gp_Trsf &); + static TopoDS_Shape moved(const TopoDS_Shape &s, const gp_Trsf &); + static TopoDS_Shape &locate(TopoDS_Shape &s, const TopLoc_Location &loc); + static TopoDS_Shape located(const TopoDS_Shape &s, const TopLoc_Location &); + static TopoDS_Shape &locate(TopoDS_Shape &s, const gp_Trsf &); + static TopoDS_Shape located(const TopoDS_Shape &s, const gp_Trsf &); + TopoShape &makeGTransform(const TopoShape &shape, const Base::Matrix4D &mat, const char *op=nullptr, bool copy=false); TopoShape makeGTransform(const Base::Matrix4D &mat, const char *op=nullptr, bool copy=false) const { @@ -388,6 +409,48 @@ public: static const std::string &shapeName(TopAbs_ShapeEnum type,bool silent=false); const std::string &shapeName(bool silent=false) const; static std::pair shapeTypeAndIndex(const char *name); + + + /** @name sub shape cached functions + * + * Mapped element names introduces some overhead when getting sub shapes + * from a shape. These functions use internal caches for sub-shape maps to + * improve performance. + */ + //@{ + void initCache(int reset=0, const char *file=nullptr, int line=0) const; + int findShape(const TopoDS_Shape &subshape) const; + TopoDS_Shape findShape(const char *name) const; + TopoDS_Shape findShape(TopAbs_ShapeEnum type, int idx) const; + int findAncestor(const TopoDS_Shape &subshape, TopAbs_ShapeEnum type) const; + TopoDS_Shape findAncestorShape(const TopoDS_Shape &subshape, TopAbs_ShapeEnum type) const; + std::vector findAncestors(const TopoDS_Shape &subshape, TopAbs_ShapeEnum type) const; + std::vector findAncestorsShapes(const TopoDS_Shape &subshape, TopAbs_ShapeEnum type) const; + /** Search sub shape + * + * unlike findShape(), the input shape does not have to be an actual + * sub-shape of this shape. The sub-shape is searched by shape geometry + * + * @param subshape: a sub shape to search + * @param names: optional output of found sub shape indexed based name + * @param checkGeometry: whether to compare shape geometry + * @param tol: tolerance to check coincident vertices + * @param atol: tolerance to check for same angles + */ + std::vector searchSubShape(const TopoShape &subshape, + std::vector *names=nullptr, + bool checkGeometry=true, + double tol=1e-7, double atol=1e-12) const; + //@} + + friend class TopoShapeCache; + +private: + // Cache storage + mutable std::shared_ptr _parentCache; + mutable std::shared_ptr _cache; + mutable TopLoc_Location _subLocation; + private: TopoDS_Shape _Shape; }; diff --git a/src/Mod/Part/App/TopoShapeCache.cpp b/src/Mod/Part/App/TopoShapeCache.cpp new file mode 100644 index 0000000000..11781c5da1 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeCache.cpp @@ -0,0 +1,25 @@ +// 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 "TopoShapeCache.h" diff --git a/src/Mod/Part/App/TopoShapeCache.h b/src/Mod/Part/App/TopoShapeCache.h new file mode 100644 index 0000000000..7109f18c7d --- /dev/null +++ b/src/Mod/Part/App/TopoShapeCache.h @@ -0,0 +1,292 @@ +// 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 * + * . * + * * + ***************************************************************************/ + +#ifndef FREECAD_TOPOSHAPECACHE_H +#define FREECAD_TOPOSHAPECACHE_H + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include "TopoShape.h" + +namespace Part +{ + +struct ShapeRelationKey { + Data::MappedName name; + bool sameType; + + ShapeRelationKey(const Data::MappedName & name, bool sameType) + :name(name), sameType(sameType) + {} + + bool operator<(const ShapeRelationKey &other) const { + if(sameType != other.sameType) + return sameType; + return name < other.name; + } +}; + +class TopoShapeCache: public std::enable_shared_from_this +{ +public: + Data::ElementMapPtr cachedElementMap; + TopLoc_Location subLocation; + TopoDS_Shape shape; + TopLoc_Location loc; + TopLoc_Location locInv; + + std::size_t memsize = 0; + + struct AncestorInfo + { + bool inited = false; + TopTools_IndexedDataMapOfShapeListOfShape shapes; + }; + class Info + { + private: + TopoShapeCache* owner = 0; + TopTools_IndexedMapOfShape shapes; + std::vector topoShapes; + std::array ancestors; + + TopoShape _getTopoShape(const TopoShape& parent, int index) + { + auto& s = topoShapes[index - 1]; + if (s.isNull()) { + s.setShape(shapes.FindKey(index), true); + s.initCache(); + s._cache->subLocation = s._Shape.Location(); + } + + if (s._Shape.IsEqual(parent._cache->shape)) + return parent; + + TopoShape res(s); + res.Tag = parent.Tag; + res.Hasher = parent.Hasher; + + if (!parent.getShape().Location().IsIdentity()) + res.setShape(TopoShape::moved(res._Shape, parent.getShape().Location()), false); + + if (s._cache->cachedElementMap) + res.resetElementMap(s._cache->cachedElementMap); + else if (parent._parentCache) { + // If no cachedElementMap exists, we use _parentCache for + // delayed generation of sub element map so that we don't need + // to always generate a full map whenever we return a sub + // shape. To simplify the mapping and avoid circular + // dependency, we do not chain parent and grandparent. + // Instead, we always use the cache from the top parent. And to + // make it work, we must accumulate the TopLoc_Location along + // the lineage, which is required for OCCT shape mapping to + // work. + // + // Cache::subLocation is shared and only contains the location + // in the direct parent shape, while TopoShape::_subLocation is + // used to accumulate locations in higher ancestors. We + // separate these two to avoid invalidating cache. + + res._subLocation = parent._subLocation * parent._cache->subLocation; + res._parentCache = parent._parentCache; + } + else + res._parentCache = owner->shared_from_this(); + return res; + } + + public: + void clear() + { + topoShapes.clear(); + } + + TopoShape getTopoShape(const TopoShape& parent, int index) + { + TopoShape res; + if (index <= 0 || index > shapes.Extent()) + return res; + topoShapes.resize(shapes.Extent()); + return _getTopoShape(parent, index); + } + + std::vector getTopoShapes(const TopoShape& parent) + { + int count = shapes.Extent(); + std::vector res; + res.reserve(count); + topoShapes.resize(count); + for (int i = 1; i <= count; ++i) + res.push_back(_getTopoShape(parent, i)); + return res; + } + + TopoDS_Shape stripLocation(const TopoDS_Shape& parent, const TopoDS_Shape& child) + { + if (parent.Location() != owner->loc) { + owner->loc = parent.Location(); + owner->locInv = parent.Location().Inverted(); + } + return TopoShape::located(child, owner->locInv * child.Location()); + } + + int find(const TopoDS_Shape& parent, const TopoDS_Shape& subshape) + { + if (parent.Location().IsIdentity()) + return shapes.FindIndex(subshape); + return shapes.FindIndex(stripLocation(parent, subshape)); + } + + TopoDS_Shape find(const TopoDS_Shape& parent, int index) + { + if (index <= 0 || index > shapes.Extent()) + return TopoDS_Shape(); + if (parent.Location().IsIdentity()) + return shapes.FindKey(index); + else + return TopoShape::moved(shapes.FindKey(index), parent.Location()); + } + + int count() const + { + return shapes.Extent(); + } + + friend TopoShapeCache; + }; + + std::array infos; + std::map> relations; + + TopoShapeCache(const TopoDS_Shape& s) + : shape(s.Located(TopLoc_Location())) + {} + + void insertRelation(const ShapeRelationKey& key, const QVector& value) + { + auto res = relations.insert(std::make_pair(key, value)); + if (res.second) + res.first->first.name.compact(); + else + res.first->second = value; + } + + bool isTouched(const TopoDS_Shape& s) + { + return !this->shape.IsPartner(s) || this->shape.Orientation() != s.Orientation(); + } + + Info& getInfo(TopAbs_ShapeEnum type) + { + auto& info = infos[type]; + if (!info.owner) { + info.owner = this; + if (!shape.IsNull()) { + if (type == TopAbs_SHAPE) { + for (TopoDS_Iterator it(shape); it.More(); it.Next()) + info.shapes.Add(it.Value()); + } + else + TopExp::MapShapes(shape, type, info.shapes); + } + } + return info; + } + + int countShape(TopAbs_ShapeEnum type) + { + if (shape.IsNull()) + return 0; + return getInfo(type).count(); + } + + int findShape(const TopoDS_Shape& parent, const TopoDS_Shape& subshape) + { + if (shape.IsNull() || subshape.IsNull()) + return 0; + return getInfo(subshape.ShapeType()).find(parent, subshape); + } + + TopoDS_Shape findShape(const TopoDS_Shape& parent, TopAbs_ShapeEnum type, int index) + { + if (!shape.IsNull()) + return getInfo(type).find(parent, index); + return TopoDS_Shape(); + } + + TopoDS_Shape findAncestor(const TopoDS_Shape& parent, + const TopoDS_Shape& subshape, + TopAbs_ShapeEnum type, + std::vector* ancestors = 0) + { + TopoDS_Shape ret; + if (shape.IsNull() || subshape.IsNull() || type == TopAbs_SHAPE) + return ret; + + auto& info = getInfo(type); + + auto& ainfo = info.ancestors[subshape.ShapeType()]; + if (!ainfo.inited) { + ainfo.inited = true; + TopExp::MapShapesAndAncestors(shape, subshape.ShapeType(), type, ainfo.shapes); + } + int index; + if (parent.Location().IsIdentity()) + index = ainfo.shapes.FindIndex(subshape); + else + index = ainfo.shapes.FindIndex(info.stripLocation(parent, subshape)); + if (!index) + return ret; + const auto& shapes = ainfo.shapes.FindFromIndex(index); + if (!shapes.Extent()) + return ret; + + if (ancestors) { + ancestors->reserve(ancestors->size() + shapes.Extent()); + for (TopTools_ListIteratorOfListOfShape it(shapes); it.More(); it.Next()) + ancestors->push_back(TopoShape::moved(it.Value(), parent.Location())); + } + return TopoShape::moved(shapes.First(), parent.Location()); + } + + std::size_t getMemSize(); +}; + +} + +#endif // FREECAD_TOPOSHAPECACHE_H diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp new file mode 100644 index 0000000000..6ade6aa2b4 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -0,0 +1,43 @@ +// 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 "TopoShape.h" +#include "TopoShapeCache.h" + +namespace Part +{ + +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(); +} + +} \ No newline at end of file