Skip to content

Commit

Permalink
Merge branch 'FreeCAD:main' into concise_toolbars
Browse files Browse the repository at this point in the history
  • Loading branch information
obelisk79 authored Dec 30, 2023
2 parents d58437d + f2fe61b commit 744da6b
Show file tree
Hide file tree
Showing 16 changed files with 1,084 additions and 309 deletions.
3 changes: 3 additions & 0 deletions src/Mod/Part/App/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 73 additions & 2 deletions src/Mod/Part/App/TopoShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Color;
namespace Part
{

class TopoShapeCache;

/* A special sub-class to indicate null shapes
*/
class PartExport NullShapeException : public Base::ValueError

Check warning on line 54 in src/Mod/Part/App/TopoShape.h

View workflow job for this annotation

GitHub Actions / Lint / Lint

class 'NullShapeException' defines a default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
Expand Down Expand Up @@ -85,6 +87,13 @@ class PartExport ShapeSegment : public Data::Segment
TopoDS_Shape Shape;
};

/// When tracing an element's history, one can either stop the trace when the element's type
/// changes, or continue tracing the history through the change. This enumeration replaces a boolean
/// parameter in the original Toponaming branch by realthunder.
enum class HistoryTraceType {
stopOnTypeChange,
followTypeChange
};


/** The representation for a CAD Shape
Expand All @@ -99,8 +108,10 @@ class PartExport TopoShape : public Data::ComplexGeoData
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 {
Expand Down Expand Up @@ -364,12 +375,29 @@ class PartExport TopoShape : public Data::ComplexGeoData
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& tds, const TopLoc_Location& loc);
static TopoDS_Shape moved(const TopoDS_Shape& tds, const TopLoc_Location& loc);
static TopoDS_Shape& move(TopoDS_Shape& tds, const gp_Trsf& transfer);
static TopoDS_Shape moved(const TopoDS_Shape& tds, const gp_Trsf& transfer);
static TopoDS_Shape& locate(TopoDS_Shape& tds, const TopLoc_Location& loc);
static TopoDS_Shape located(const TopoDS_Shape& tds, const TopLoc_Location& loc);
static TopoDS_Shape& locate(TopoDS_Shape& tds, const gp_Trsf& transfer);
static TopoDS_Shape located(const TopoDS_Shape& tds, const gp_Trsf& transfer);

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 {
Expand All @@ -388,6 +416,49 @@ class PartExport TopoShape : public Data::ComplexGeoData
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;
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
*/
// TODO: Implement this method and its tests later in Toponaming Phase 3.
//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;
};
Expand Down
255 changes: 255 additions & 0 deletions src/Mod/Part/App/TopoShapeCache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/****************************************************************************
* *
* Copyright (c) 2022 Zheng, Lei <[email protected]> *
* 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());
}
Loading

0 comments on commit 744da6b

Please sign in to comment.