Files
create/src/Mod/Part/App/TopoShapeCache.cpp
Chris Hennes e5b95c7522 Part/Toponaming: Clang-tidy cleanup of TopoShapeCache
Part/Toponaming: Move implementions to cpp file
Part/Toponaming: Automated code cleanup
Part/Toponaming: Address boolean blindess with HistoryTraceType
Part/Toponaming: More linter cleanup
Part/Toponaming: Add located and moved methods
Part/Toponaming: Moved and Located
Part/Toponaming: Add missing methods
Part/Toponaming: Add test framework for TopoShapeCache
Part/Toponaming: Add documentation provided by realthunder
Part/Toponaming: Extend tests for TopoShapeCache
Part/Toponaming: Rename getInfo to getAncestry
Part/Toponaming: Fix bug with newer OCCT
Part/Toponaming: Add final tests and tweak naming
Part/Toponaming: Address reviewer comments
2023-12-27 21:16:15 -06:00

256 lines
8.8 KiB
C++

// 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 "PreCompiled.h"
#include "TopoShapeCache.h"
using namespace Part;
ShapeRelationKey::ShapeRelationKey(Data::MappedName name, HistoryTraceType historyTraceType)
: name(std::move(name))
, historyTraceType(historyTraceType)
{}
bool ShapeRelationKey::operator<(const ShapeRelationKey& other) const
{
if (historyTraceType != other.historyTraceType) {
return historyTraceType < other.historyTraceType;
}
return name < other.name;
}
TopoShape TopoShapeCache::Ancestry::_getTopoShape(const TopoShape& parent, int index)
{
auto& ts = topoShapes[index - 1];
if (ts.isNull()) {
ts.setShape(shapes.FindKey(index), true);
ts.initCache();
ts._cache->subLocation = ts._Shape.Location();
}
if (ts._Shape.IsEqual(parent._cache->shape)) {
return parent;
}
TopoShape res(ts);
res.Tag = parent.Tag;
res.Hasher = parent.Hasher;
if (!parent.getShape().Location().IsIdentity()) {
res.setShape(TopoShape::moved(res._Shape, parent.getShape().Location()), false);
}
if (ts._cache->cachedElementMap) {
res.resetElementMap(ts._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;
}
void TopoShapeCache::Ancestry::clear()
{
topoShapes.clear();
}
TopoShape TopoShapeCache::Ancestry::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> TopoShapeCache::Ancestry::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 TopoShapeCache::Ancestry::stripLocation(const TopoDS_Shape& parent,
const TopoDS_Shape& child)
{
if (parent.Location() != owner->location) {
owner->location = parent.Location();
owner->locationInverse = parent.Location().Inverted();
}
return TopoShape::located(child, owner->locationInverse * child.Location());
}
int TopoShapeCache::Ancestry::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 TopoShapeCache::Ancestry::find(const TopoDS_Shape& parent, int index)
{
if (index <= 0 || index > shapes.Extent()) {
return {};
}
if (parent.Location().IsIdentity()) {
return shapes.FindKey(index);
}
return TopoShape::moved(shapes.FindKey(index), parent.Location());
}
int TopoShapeCache::Ancestry::count() const
{
return shapes.Extent();
}
TopoShapeCache::TopoShapeCache(const TopoDS_Shape& tds)
: shape(tds.Located(TopLoc_Location()))
{}
void TopoShapeCache::insertRelation(const ShapeRelationKey& key,
const QVector<Data::MappedElement>& value)
{
auto [insertedItr, newKeyInserted] = relations.insert({key, value});
if (newKeyInserted) {
insertedItr->first.name.compact();
}
else {
insertedItr->second = value;
}
}
bool TopoShapeCache::isTouched(const TopoDS_Shape& tds) const
{
return !this->shape.IsPartner(tds) || this->shape.Orientation() != tds.Orientation();
}
TopoShapeCache::Ancestry& TopoShapeCache::getAncestry(TopAbs_ShapeEnum type)
{
auto& ancestry = shapeAncestryCache.at(type);
if (!ancestry.owner) {
ancestry.owner = this;
if (!shape.IsNull()) {
if (type == TopAbs_SHAPE) {
for (TopoDS_Iterator it(shape); it.More(); it.Next()) {
ancestry.shapes.Add(it.Value());
}
}
else {
TopExp::MapShapes(shape, type, ancestry.shapes);
}
}
}
return ancestry;
}
int TopoShapeCache::countShape(TopAbs_ShapeEnum type)
{
if (shape.IsNull()) {
return 0;
}
return getAncestry(type).count();
}
int TopoShapeCache::findShape(const TopoDS_Shape& parent, const TopoDS_Shape& subShape)
{
if (shape.IsNull() || subShape.IsNull()) {
return 0;
}
return getAncestry(subShape.ShapeType()).find(parent, subShape);
}
TopoDS_Shape TopoShapeCache::findShape(const TopoDS_Shape& parent, TopAbs_ShapeEnum type, int index)
{
if (!shape.IsNull()) {
return getAncestry(type).find(parent, index);
}
return {};
}
TopoDS_Shape TopoShapeCache::findAncestor(const TopoDS_Shape& parent,
const TopoDS_Shape& subShape,
TopAbs_ShapeEnum type,
std::vector<TopoDS_Shape>* ancestors)
{
TopoDS_Shape nullShape;
if (shape.IsNull() || subShape.IsNull() || type == TopAbs_SHAPE) {
return nullShape;
}
auto& info = getAncestry(type);
auto& ancestorInfo = info.ancestors.at(subShape.ShapeType());
if (!ancestorInfo.initialized) {
ancestorInfo.initialized = true;
// ancestorInfo.shapes is the output variable here, storing (and caching) the actual map
TopExp::MapShapesAndAncestors(shape, subShape.ShapeType(), type, ancestorInfo.shapes);
}
int index = parent.Location().IsIdentity()
? ancestorInfo.shapes.FindIndex(subShape)
: ancestorInfo.shapes.FindIndex(info.stripLocation(parent, subShape));
if (index == 0) {
return nullShape;
}
const auto& shapes = ancestorInfo.shapes.FindFromIndex(index);
if (shapes.Extent() == 0) {
return nullShape;
}
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());
}