Part/Toponaming: Add TopoShapeCache base implementation

Copied from RealThunder's TopoNaming branch and modified only as much as required to enable compilation.
This commit is contained in:
Chris Hennes
2023-11-18 11:28:59 -06:00
committed by Chris Hennes
parent 0c1f6138b4
commit 4077c55fae
5 changed files with 428 additions and 2 deletions

View File

@@ -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

View File

@@ -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<TopAbs_ShapeEnum,int> 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<int> findAncestors(const TopoDS_Shape &subshape, TopAbs_ShapeEnum type) const;
std::vector<TopoDS_Shape> 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<TopoShape> searchSubShape(const TopoShape &subshape,
std::vector<std::string> *names=nullptr,
bool checkGeometry=true,
double tol=1e-7, double atol=1e-12) const;
//@}
friend class TopoShapeCache;
private:
// Cache storage
mutable std::shared_ptr<TopoShapeCache> _parentCache;
mutable std::shared_ptr<TopoShapeCache> _cache;
mutable TopLoc_Location _subLocation;
private:
TopoDS_Shape _Shape;
};

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2022 Zheng, Lei <realthunder.dev@gmail.com> *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#include "TopoShapeCache.h"

View File

@@ -0,0 +1,292 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2022 Zheng, Lei <realthunder.dev@gmail.com> *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef FREECAD_TOPOSHAPECACHE_H
#define FREECAD_TOPOSHAPECACHE_H
#include "PreCompiled.h"
#ifndef _PreComp_
# include <TopoDS.hxx>
# include <TopoDS_Compound.hxx>
# include <TopoDS_Iterator.hxx>
# include <TopoDS_Solid.hxx>
# include <TopoDS_Vertex.hxx>
# include <TopExp.hxx>
# include <TopExp_Explorer.hxx>
# include <TopTools_ListIteratorOfListOfShape.hxx>
#endif
#include <App/ElementMap.h>
#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<TopoShapeCache>
{
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<TopoShape> topoShapes;
std::array<AncestorInfo, TopAbs_SHAPE + 1> 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<TopoShape> getTopoShapes(const TopoShape& parent)
{
int count = shapes.Extent();
std::vector<TopoShape> 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<Info, TopAbs_SHAPE + 1> infos;
std::map<ShapeRelationKey, QVector<Data::MappedElement>> relations;
TopoShapeCache(const TopoDS_Shape& s)
: shape(s.Located(TopLoc_Location()))
{}
void insertRelation(const ShapeRelationKey& key, const QVector<Data::MappedElement>& 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<TopoDS_Shape>* 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

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2022 Zheng, Lei <realthunder.dev@gmail.com> *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#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();
}
}