Merge pull request #12943 from bgbsww/bgbsww-toponamingFeatureChamfer

Toponaming/part features chamfer, fillet; dependencies and test updates
This commit is contained in:
Chris Hennes
2024-03-19 16:53:30 -05:00
committed by GitHub
11 changed files with 502 additions and 68 deletions

View File

@@ -52,7 +52,7 @@ using BoundBox3d = BoundBox3<double>;
namespace Data
{
struct MappedChildElements;
//struct MappedChildElements;
/** Segments
* Sub-element type of the ComplexGeoData type

View File

@@ -129,12 +129,16 @@ public:
/// include the index.
///
/// \param buffer A (possibly non-empty) string buffer to append the name to.
void appendToStringBuffer(std::string & buffer) const
/// \return A const char pointer to the name we appended to the buffer.
const char * appendToStringBuffer(std::string & buffer) const
{
// Note! buffer is not cleared on purpose.
std::size_t offset = buffer.size();
buffer += this->type;
if (this->index > 0) {
buffer += std::to_string(this->index);
}
return buffer.c_str() + offset;
}
/// Create and return a new std::string with this name in it.

View File

@@ -32,6 +32,7 @@
#endif
#include "FeatureChamfer.h"
#include "TopoShapeOpCode.h"
using namespace Part;
@@ -47,12 +48,14 @@ App::DocumentObjectExecReturn *Chamfer::execute()
return new App::DocumentObjectExecReturn("No object linked");
try {
TopoShape baseTopoShape = Feature::getTopoShape(link);
auto baseShape = Feature::getShape(link);
BRepFilletAPI_MakeChamfer mkChamfer(baseShape);
TopTools_IndexedMapOfShape mapOfEdges;
TopTools_IndexedDataMapOfShapeListOfShape mapEdgeFace;
TopExp::MapShapesAndAncestors(baseShape, TopAbs_EDGE, TopAbs_FACE, mapEdgeFace);
TopTools_IndexedMapOfShape mapOfEdges;
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges);
#ifndef FC_USE_TNP_FIX
std::vector<FilletElement> values = Edges.getValues();
for (const auto & value : values) {
@@ -89,6 +92,39 @@ App::DocumentObjectExecReturn *Chamfer::execute()
prop.touch();
return App::DocumentObject::StdReturn;
#else
const auto &vals = EdgeLinks.getSubValues();
const auto &subs = EdgeLinks.getShadowSubs();
if(subs.size()!=(size_t)Edges.getSize())
return new App::DocumentObjectExecReturn("Edge link size mismatch");
size_t i=0;
for(const auto &info : Edges.getValues()) {
auto &sub = subs[i];
auto &ref = sub.first.size()?sub.first:vals[i];
++i;
// Toponaming project March 2024: Replaced this code because it wouldn't work:
// TopoDS_Shape edge;
// try {
// edge = baseTopoShape.getSubShape(ref.c_str());
// }catch(...){}
auto id = Data::MappedName(ref.c_str()).toIndexedName().getIndex();
const TopoDS_Edge& edge = TopoDS::Edge(mapOfEdges.FindKey(id));
if(edge.IsNull())
return new App::DocumentObjectExecReturn("Invalid edge link");
double radius1 = info.radius1;
double radius2 = info.radius2;
const TopoDS_Face& face = TopoDS::Face(mapEdgeFace.FindFromKey(edge).First());
mkChamfer.Add(radius1, radius2, TopoDS::Edge(edge), face);
}
TopoDS_Shape shape = mkChamfer.Shape();
if (shape.IsNull())
return new App::DocumentObjectExecReturn("Resulting shape is null");
TopoShape res(0);
this->Shape.setValue(res.makeElementShape(mkChamfer,baseTopoShape,Part::OpCodes::Chamfer));
return Part::Feature::execute();
#endif
}
catch (Standard_Failure& e) {
return new App::DocumentObjectExecReturn(e.GetMessageString());

View File

@@ -34,6 +34,7 @@
#include <Base/Exception.h>
#include "FeatureFillet.h"
#include "TopoShapeOpCode.h"
using namespace Part;
@@ -48,15 +49,19 @@ App::DocumentObjectExecReturn *Fillet::execute()
if (!link)
return new App::DocumentObjectExecReturn("No object linked");
auto baseShape = Feature::getShape(link);
try {
#if defined(__GNUC__) && defined (FC_OS_LINUX)
Base::SignalException se;
#endif
auto baseShape = Feature::getShape(link);
TopoShape baseTopoShape = Feature::getTopoShape(link);
BRepFilletAPI_MakeFillet mkFillet(baseShape);
TopTools_IndexedMapOfShape mapOfShape;
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfShape);
TopTools_IndexedMapOfShape mapOfEdges;
TopExp::MapShapes(baseShape, TopAbs_EDGE, mapOfEdges);
#ifndef FC_USE_TNP_FIX
std::vector<FilletElement> values = Edges.getValues();
for (const auto & value : values) {
@@ -92,6 +97,38 @@ App::DocumentObjectExecReturn *Fillet::execute()
prop.touch();
return App::DocumentObject::StdReturn;
#else
const auto &vals = EdgeLinks.getSubValues();
const auto &subs = EdgeLinks.getShadowSubs();
if(subs.size()!=(size_t)Edges.getSize())
return new App::DocumentObjectExecReturn("Edge link size mismatch");
size_t i=0;
for(const auto &info : Edges.getValues()) {
auto &sub = subs[i];
auto &ref = sub.first.size()?sub.first:vals[i];
++i;
// Toponaming project March 2024: Replaced this code because it wouldn't work:
// TopoDS_Shape edge;
// try {
// edge = baseTopoShape.getSubShape(ref.c_str());
// }catch(...){}
auto id = Data::MappedName(ref.c_str()).toIndexedName().getIndex();
const TopoDS_Edge& edge = TopoDS::Edge(mapOfEdges.FindKey(id));
if(edge.IsNull())
return new App::DocumentObjectExecReturn("Invalid edge link");
double radius1 = info.radius1;
double radius2 = info.radius2;
mkFillet.Add(radius1, radius2, TopoDS::Edge(edge));
}
TopoDS_Shape shape = mkFillet.Shape();
if (shape.IsNull())
return new App::DocumentObjectExecReturn("Resulting shape is null");
TopoShape res(0);
this->Shape.setValue(res.makeElementShape(mkFillet,baseTopoShape,Part::OpCodes::Fillet));
return Part::Feature::execute();
#endif
}
catch (Standard_Failure& e) {
return new App::DocumentObjectExecReturn(e.GetMessageString());

View File

@@ -1217,16 +1217,69 @@ FilletBase::FilletBase()
{
ADD_PROPERTY(Base,(nullptr));
ADD_PROPERTY(Edges,(0,0,0));
ADD_PROPERTY_TYPE(EdgeLinks,(0), 0,
(App::PropertyType)(App::Prop_ReadOnly|App::Prop_Hidden),0);
Edges.setSize(0);
}
short FilletBase::mustExecute() const
{
if (Base.isTouched() || Edges.isTouched())
if (Base.isTouched() || Edges.isTouched() || EdgeLinks.isTouched())
return 1;
return 0;
}
void FilletBase::onChanged(const App::Property *prop) {
if(getDocument() && !getDocument()->testStatus(App::Document::Restoring)) {
if(prop == &Edges || prop == &Base) {
if(!prop->testStatus(App::Property::User3))
syncEdgeLink();
}
}
Feature::onChanged(prop);
}
void FilletBase::onDocumentRestored() {
if(EdgeLinks.getSubValues().empty())
syncEdgeLink();
Feature::onDocumentRestored();
}
void FilletBase::syncEdgeLink() {
if(!Base.getValue() || !Edges.getSize()) {
EdgeLinks.setValue(0);
return;
}
std::vector<std::string> subs;
std::string sub("Edge");
for(auto &info : Edges.getValues())
subs.emplace_back(sub+std::to_string(info.edgeid));
EdgeLinks.setValue(Base.getValue(),subs);
}
void FilletBase::onUpdateElementReference(const App::Property *prop) {
if(prop!=&EdgeLinks || !getNameInDocument())
return;
auto values = Edges.getValues();
const auto &subs = EdgeLinks.getSubValues();
for(size_t i=0;i<values.size();++i) {
if(i>=subs.size()) {
FC_WARN("fillet edge count mismatch in object " << getFullName());
break;
}
int idx = 0;
sscanf(subs[i].c_str(),"Edge%d",&idx);
if(idx)
values[i].edgeid = idx;
else
FC_WARN("invalid fillet edge link '" << subs[i] << "' in object "
<< getFullName());
}
Edges.setStatus(App::Property::User3,true);
Edges.setValues(values);
Edges.setStatus(App::Property::User3,false);
}
// ---------------------------------------------------------
PROPERTY_SOURCE(Part::FeatureExt, Part::Feature)

View File

@@ -175,8 +175,15 @@ public:
App::PropertyLink Base;
PropertyFilletEdges Edges;
App::PropertyLinkSub EdgeLinks;
short mustExecute() const override;
void onUpdateElementReference(const App::Property *prop) override;
protected:
void onDocumentRestored() override;
void onChanged(const App::Property *) override;
void syncEdgeLink();
};
using FeaturePython = App::FeaturePythonT<Feature>;

View File

@@ -336,6 +336,7 @@ Data::Segment* TopoShape::getSubElement(const char* Type, unsigned long n) const
return new ShapeSegment(getSubShape(temp.c_str()));
}
// Type can be (should be?) a subshape name, not a type, E.G. Edge3
TopoDS_Shape TopoShape::getSubShape(const char* Type, bool silent) const {
TopoShape s(*this);
s.Tag = 0;

View File

@@ -102,6 +102,7 @@
#include "Geometry.h"
#include "BRepOffsetAPI_MakeOffsetFix.h"
#include <App/ElementMap.h>
#include <App/ElementNamingUtils.h>
#include <ShapeAnalysis_FreeBoundsProperties.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
@@ -271,7 +272,7 @@ TopoDS_Shape TopoShape::located(const TopoDS_Shape& tds, const gp_Trsf& transfer
return moved(sCopy, transfer);
}
void TopoShape::operator = (const TopoShape& sh)
void TopoShape::operator=(const TopoShape& sh)
{
if (this != &sh) {
this->setShape(sh._Shape, true);
@@ -814,6 +815,7 @@ void TopoShape::mapSubElementForShape(const TopoShape& other, const char* op)
void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool forceHasher)
{
#ifndef FC_USE_TNP_FIX
if (!canMapElement(other)) {
return;
}
@@ -831,6 +833,115 @@ void TopoShape::mapSubElement(const TopoShape& other, const char* op, bool force
}
mapSubElementForShape(other, op);
#else
if (!canMapElement(other)) {
return;
}
if (!getElementMapSize(false) && this->_Shape.IsPartner(other._Shape)) {
if (!this->Hasher) {
this->Hasher = other.Hasher;
}
copyElementMap(other, op);
return;
}
bool warned = false;
static const std::array<TopAbs_ShapeEnum, 3> types = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE};
auto checkHasher = [this](const TopoShape& other) {
if (Hasher) {
if (other.Hasher != Hasher) {
if (!getElementMapSize(false)) {
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
FC_WARN("hasher mismatch");
}
}
else {
// FC_THROWM(Base::RuntimeError, "hasher mismatch");
FC_ERR("hasher mismatch");
}
Hasher = other.Hasher;
}
}
else {
Hasher = other.Hasher;
}
};
for (auto type : types) {
auto& shapeMap = _cache->getAncestry(type);
auto& otherMap = other._cache->getAncestry(type);
if (!shapeMap.count() || !otherMap.count()) {
continue;
}
if (!forceHasher && other.Hasher) {
forceHasher = true;
checkHasher(other);
}
const char* shapetype = shapeName(type).c_str();
std::ostringstream ss;
bool forward;
int count;
if (otherMap.count() <= shapeMap.count()) {
forward = true;
count = otherMap.count();
}
else {
forward = false;
count = shapeMap.count();
}
for (int k = 1; k <= count; ++k) {
int i, idx;
if (forward) {
i = k;
idx = shapeMap.find(_Shape, otherMap.find(other._Shape, k));
if (!idx) {
continue;
}
}
else {
idx = k;
i = otherMap.find(other._Shape, shapeMap.find(_Shape, k));
if (!i) {
continue;
}
}
Data::IndexedName element = Data::IndexedName::fromConst(shapetype, idx);
for (auto& v :
other.getElementMappedNames(Data::IndexedName::fromConst(shapetype, i), true)) {
auto& name = v.first;
auto& sids = v.second;
if (sids.size()) {
if (!Hasher) {
Hasher = sids[0].getHasher();
}
else if (!sids[0].isFromSameHasher(Hasher)) {
if (!warned) {
warned = true;
FC_WARN("hasher mismatch");
}
sids.clear();
}
}
ss.str("");
// Originally in ComplexGeoData::setElementName
// LinkStable/src/App/ComplexGeoData.cpp#L1631
// No longer possible after map separated in ElementMap.cpp
if (!elementMap()) {
resetElementMap(std::make_shared<Data::ElementMap>());
}
elementMap()->encodeElementName(shapetype[0], name, ss, &sids, Tag, op, other.Tag);
elementMap()->setElementName(element, name, Tag, &sids);
}
}
}
#endif
}
void TopoShape::mapSubElementsTo(std::vector<TopoShape>& shapes, const char* op) const
@@ -892,6 +1003,7 @@ void TopoShape::mapCompoundSubElements(const std::vector<TopoShape>& shapes, con
void TopoShape::mapSubElement(const std::vector<TopoShape>& shapes, const char* op)
{
#ifndef FC_USE_TNP_FIX
if (shapes.empty()) {
return;
}
@@ -904,6 +1016,59 @@ void TopoShape::mapSubElement(const std::vector<TopoShape>& shapes, const char*
mapSubElement(shape, op);
}
}
#else
if (shapes.empty()) {
return;
}
if (shapeType(true) == TopAbs_COMPOUND) {
int count = 0;
for (auto& s : shapes) {
if (s.isNull()) {
continue;
}
if (!getSubShape(TopAbs_SHAPE, ++count, true).IsPartner(s._Shape)) {
count = 0;
break;
}
}
if (count) {
std::vector<Data::ElementMap::MappedChildElements> children;
children.reserve(count * 3);
TopAbs_ShapeEnum types[] = {TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE};
for (unsigned i = 0; i < sizeof(types) / sizeof(types[0]); ++i) {
int offset = 0;
for (auto& s : shapes) {
if (s.isNull()) {
continue;
}
int count = s.countSubShapes(types[i]);
if (!count) {
continue;
}
children.emplace_back();
auto& child = children.back();
child.indexedName =
Data::IndexedName::fromConst(shapeName(types[i]).c_str(), 1);
child.offset = offset;
offset += count;
child.count = count;
child.elementMap = s.elementMap();
child.tag = s.Tag;
if (op) {
child.postfix = op;
}
}
}
setMappedChildElements(children);
return;
}
}
for (auto& shape : shapes) {
mapSubElement(shape, op);
}
#endif
}
std::vector<TopoDS_Shape> TopoShape::getSubShapes(TopAbs_ShapeEnum type,
@@ -3361,18 +3526,20 @@ struct MapperThruSections: MapperMaker
}
};
struct MapperPrism: MapperMaker {
struct MapperPrism: MapperMaker
{
std::unordered_map<TopoDS_Shape, TopoDS_Shape, ShapeHasher, ShapeHasher> vertexMap;
ShapeMapper::ShapeMap edgeMap;
MapperPrism(BRepFeat_MakePrism &maker, const TopoShape &upTo)
:MapperMaker(maker)
MapperPrism(BRepFeat_MakePrism& maker, const TopoShape& upTo)
: MapperMaker(maker)
{
(void)upTo;
std::vector<TopoShape> shapes;
for(TopTools_ListIteratorOfListOfShape it(maker.FirstShape());it.More();it.Next())
for (TopTools_ListIteratorOfListOfShape it(maker.FirstShape()); it.More(); it.Next()) {
shapes.push_back(it.Value());
}
if (shapes.size()) {
// It seems that BRepFeat_MakePrism::newEdges() does not return
@@ -3382,18 +3549,21 @@ struct MapperPrism: MapperMaker {
// i.e. the bottom profile, and add all edges that shares a
// vertex with the profiles as new edges.
std::unordered_set<TopoDS_Shape,ShapeHasher,ShapeHasher> edgeSet;
std::unordered_set<TopoDS_Shape, ShapeHasher, ShapeHasher> edgeSet;
TopoShape bottom;
bottom.makeElementCompound(shapes, nullptr, TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
bottom.makeElementCompound(shapes,
nullptr,
TopoShape::SingleShapeCompoundCreationPolicy::returnShape);
TopoShape shape(maker.Shape());
for (auto &vertex : bottom.getSubShapes(TopAbs_VERTEX)) {
for (auto &e : shape.findAncestorsShapes(vertex, TopAbs_EDGE)) {
for (auto& vertex : bottom.getSubShapes(TopAbs_VERTEX)) {
for (auto& e : shape.findAncestorsShapes(vertex, TopAbs_EDGE)) {
// Make sure to not visit the the same edge twice.
// And check only edge that are not found in the bottom profile
if (!edgeSet.insert(e).second && !bottom.findShape(e)) {
auto otherVertex = TopExp::FirstVertex(TopoDS::Edge(e));
if (otherVertex.IsSame(vertex))
if (otherVertex.IsSame(vertex)) {
otherVertex = TopExp::LastVertex(TopoDS::Edge(e));
}
vertexMap[vertex] = otherVertex;
}
}
@@ -3405,37 +3575,44 @@ struct MapperPrism: MapperMaker {
// corresponding edges in the top profile, what an extra criteria
// for disambiguation. That is, the pair of edges (bottom and top)
// must belong to the same face.
for (auto &edge : bottom.getSubShapes(TopAbs_EDGE)) {
for (auto& edge : bottom.getSubShapes(TopAbs_EDGE)) {
std::vector<int> indices;
auto first = TopExp::FirstVertex(TopoDS::Edge(edge));
auto last = TopExp::LastVertex(TopoDS::Edge(edge));
auto itFirst = vertexMap.find(first);
auto itLast = vertexMap.find(last);
if (itFirst == vertexMap.end() || itLast ==vertexMap.end())
if (itFirst == vertexMap.end() || itLast == vertexMap.end()) {
continue;
}
std::vector<TopoShape> faces;
for (int idx : shape.findAncestors(edge, TopAbs_FACE))
for (int idx : shape.findAncestors(edge, TopAbs_FACE)) {
faces.push_back(shape.getSubTopoShape(TopAbs_FACE, idx));
if (faces.empty())
}
if (faces.empty()) {
continue;
}
for (int idx : shape.findAncestors(itFirst->second, TopAbs_EDGE)) {
auto e = shape.getSubTopoShape(TopAbs_EDGE, idx);
if (!e.findShape(itLast->second))
if (!e.findShape(itLast->second)) {
continue;
for (auto &face : faces) {
if (!face.findShape(e.getShape()))
}
for (auto& face : faces) {
if (!face.findShape(e.getShape())) {
continue;
auto &entry = edgeMap[edge];
if (entry.shapeSet.insert(e.getShape()).second)
}
auto& entry = edgeMap[edge];
if (entry.shapeSet.insert(e.getShape()).second) {
entry.shapes.push_back(e.getShape());
}
}
}
}
}
}
virtual const std::vector<TopoDS_Shape> &generated(const TopoDS_Shape &s) const override {
virtual const std::vector<TopoDS_Shape>& generated(const TopoDS_Shape& s) const override
{
_res.clear();
switch(s.ShapeType()) {
switch (s.ShapeType()) {
case TopAbs_VERTEX: {
auto it = vertexMap.find(s);
if (it != vertexMap.end()) {
@@ -3446,9 +3623,10 @@ struct MapperPrism: MapperMaker {
}
case TopAbs_EDGE: {
auto it = edgeMap.find(s);
if (it != edgeMap.end())
return it->second.shapes;
break;
if (it != edgeMap.end()) {
return it->second.shapes;
}
break;
}
default:
break;
@@ -3651,7 +3829,7 @@ TopoShape& TopoShape::makeElementFilledFace(const std::vector<TopoShape>& _shape
// TODO: This method does not appear to ever be called in the codebase, and it is probably
// broken, because using TopoShape() with no parameters means the result will not have an
// element Map.
//TopoShape& TopoShape::makeElementSolid(const std::vector<TopoShape>& shapes, const char* op)
// TopoShape& TopoShape::makeElementSolid(const std::vector<TopoShape>& shapes, const char* op)
//{
// return makeElementSolid(TopoShape().makeElementCompound(shapes), op);
//}
@@ -3951,22 +4129,25 @@ TopoShape& TopoShape::makeElementShape(BRepBuilderAPI_MakeShape& mkShape,
{
TopoDS_Shape shape;
// OCCT 7.3.x requires calling Solid() and not Shape() to function correctly
if ( typeid(mkShape) == typeid(BRepPrimAPI_MakeHalfSpace) ) {
if (typeid(mkShape) == typeid(BRepPrimAPI_MakeHalfSpace)) {
shape = static_cast<BRepPrimAPI_MakeHalfSpace&>(mkShape).Solid();
} else {
}
else {
shape = mkShape.Shape();
}
return makeShapeWithElementMap(shape, MapperMaker(mkShape), shapes, op);
}
TopoShape &TopoShape::makeElementShape(BRepFeat_MakePrism &mkShape,
const std::vector<TopoShape> &sources,
const TopoShape &upTo,
const char *op)
TopoShape& TopoShape::makeElementShape(BRepFeat_MakePrism& mkShape,
const std::vector<TopoShape>& sources,
const TopoShape& upTo,
const char* op)
{
if(!op) op = Part::OpCodes::Prism;
if (!op) {
op = Part::OpCodes::Prism;
}
MapperPrism mapper(mkShape, upTo);
makeShapeWithElementMap(mkShape.Shape(),mapper,sources,op);
makeShapeWithElementMap(mkShape.Shape(), mapper, sources, op);
return *this;
}
@@ -4052,7 +4233,7 @@ TopoShape& TopoShape::makeElementPrism(const TopoShape& base, const gp_Vec& vec,
// TODO: This code was transferred in Feb 2024 as part of the toponaming project, but appears to be
// unused. It is potentially useful if debugged.
//TopoShape& TopoShape::makeElementPrismUntil(const TopoShape& _base,
// TopoShape& TopoShape::makeElementPrismUntil(const TopoShape& _base,
// const TopoShape& profile,
// const TopoShape& supportFace,
// const TopoShape& __uptoface,
@@ -4121,9 +4302,9 @@ TopoShape& TopoShape::makeElementPrism(const TopoShape& base, const gp_Vec& vec,
// }
//
// if (remove_limits) {
// // Note: Using an unlimited face every time gives unnecessary failures for concave faces
// TopLoc_Location loc = face.Location();
// BRepAdaptor_Surface adapt(face, Standard_False);
// // Note: Using an unlimited face every time gives unnecessary failures for concave
// faces TopLoc_Location loc = face.Location(); BRepAdaptor_Surface adapt(face,
// Standard_False);
// // use the placement of the adapter, not of the upToFace
// loc = TopLoc_Location(adapt.Trsf());
// BRepBuilderAPI_MakeFace mkFace(adapt.Surface().Surface(), Precision::Confusion());
@@ -4656,7 +4837,7 @@ TopoShape& TopoShape::makeElementBSplineFace(const std::vector<TopoShape>& input
}
unsigned ind = 0;
for (auto& edge : newEdges) {
if ( ind < edges.size() ) {
if (ind < edges.size()) {
edge.resetElementMap(edges[ind++].elementMap());
}
}
@@ -5510,33 +5691,32 @@ TopoShape& TopoShape::makeElementBoolean(const char* maker,
return *this;
}
bool TopoShape::isSame(const Data::ComplexGeoData &_other) const
bool TopoShape::isSame(const Data::ComplexGeoData& _other) const
{
if(!_other.isDerivedFrom(TopoShape::getClassTypeId()))
return false;
const auto &other = static_cast<const TopoShape &>(_other);
return Tag == other.Tag
&& Hasher == other.Hasher
&& _Shape.IsEqual(other._Shape);
}
void TopoShape::cacheRelatedElements(const Data::MappedName &name,
HistoryTraceType sameType,
const QVector<Data::MappedElement> & names) const
{
initCache();
_cache->insertRelation(ShapeRelationKey(name,sameType), names);
}
bool TopoShape::getRelatedElementsCached(const Data::MappedName &name,
HistoryTraceType sameType,
QVector<Data::MappedElement> &names) const
{
if(!_cache) {
if (!_other.isDerivedFrom(TopoShape::getClassTypeId())) {
return false;
}
auto it = _cache->relations.find(ShapeRelationKey(name,sameType));
if(it == _cache->relations.end()) {
const auto& other = static_cast<const TopoShape&>(_other);
return Tag == other.Tag && Hasher == other.Hasher && _Shape.IsEqual(other._Shape);
}
void TopoShape::cacheRelatedElements(const Data::MappedName& name,
HistoryTraceType sameType,
const QVector<Data::MappedElement>& names) const
{
initCache();
_cache->insertRelation(ShapeRelationKey(name, sameType), names);
}
bool TopoShape::getRelatedElementsCached(const Data::MappedName& name,
HistoryTraceType sameType,
QVector<Data::MappedElement>& names) const
{
if (!_cache) {
return false;
}
auto it = _cache->relations.find(ShapeRelationKey(name, sameType));
if (it == _cache->relations.end()) {
return false;
}
names = it->second;

View File

@@ -70,6 +70,7 @@
#include "SoBrepFaceSet.h"
#include "SoBrepPointSet.h"
FC_LOG_LEVEL_INIT("Part", true, true)
using namespace PartGui;
namespace sp = std::placeholders;
@@ -592,6 +593,16 @@ void DlgFilletEdges::setupFillet(const std::vector<App::DocumentObject*>& objs)
{
App::DocumentObject* base = d->fillet->Base.getValue();
const std::vector<Part::FilletElement>& e = d->fillet->Edges.getValues();
const auto &subs = d->fillet->EdgeLinks.getShadowSubs();
if(subs.size()!=e.size()) {
FC_ERR("edge link size mismatch");
return;
}
std::set<std::string> subSet;
for(auto &sub : subs)
subSet.insert(sub.first.empty()?sub.second:sub.first);
std::string tmp;
std::vector<App::DocumentObject*>::const_iterator it = std::find(objs.begin(), objs.end(), base);
if (it != objs.end()) {
// toggle visibility
@@ -613,6 +624,40 @@ void DlgFilletEdges::setupFillet(const std::vector<App::DocumentObject*>& objs)
std::vector<std::string> subElements;
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->treeView->model());
bool block = model->blockSignals(true); // do not call toggleCheckState
auto baseShape = Part::Feature::getTopoShape(base);
std::set<Part::FilletElement> elements;
for(size_t i=0;i<e.size();++i) {
auto &sub = subs[i];
if(sub.first.empty()) {
int idx = 0;
sscanf(sub.second.c_str(),"Edge%d",&idx);
if(idx==0)
FC_WARN("missing element reference: " << sub.second);
else
elements.insert(e[i]);
continue;
}
auto &ref = sub.first;
Part::TopoShape edge;
try {
edge = baseShape.getSubShape(ref.c_str());
}catch(...) {}
if(!edge.isNull()) {
elements.insert(e[i]);
continue;
}
FC_WARN("missing element reference: " << base->getFullName() << "." << ref);
for(auto &mapped : Part::Feature::getRelatedElements(base,ref.c_str())) {
tmp.clear();
if(!subSet.insert(mapped.index.appendToStringBuffer(tmp)).second
|| !subSet.insert(mapped.name.toString(0)).second)
continue;
FC_WARN("guess element reference: " << ref << " -> " << mapped.index);
elements.emplace(mapped.index.getIndex(),e[i].radius1,e[i].radius2);
}
}
for (const auto & et : e) {
std::vector<int>::iterator it = std::find(d->edge_ids.begin(), d->edge_ids.end(), et.edgeid);
if (it != d->edge_ids.end()) {

View File

@@ -71,7 +71,6 @@ TEST_F(FeatureChamferTest, testOther)
// Assert
EXPECT_EQ(sec, 24);
// Act
// _chamfer->Edges.setValues(PartTestHelpers::_getFilletEdges({1, 2}, chamfer, chamfer));
_chamfer->Edges.setValues(PartTestHelpers::_getFilletEdges({1, 2}, chamfer, chamfer));
double fusedVolume = PartTestHelpers::getVolume(_fused->Shape.getValue());
double chamferVolume = PartTestHelpers::getVolume(_chamfer->Shape.getValue());

View File

@@ -1422,12 +1422,16 @@ TEST_F(TopoShapeExpansionTest, makeElementBooleanCut)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 38);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;CUT;:H1:7,F;:U;CUT;:H1:7,E;:L(Face5;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E|Face5;:M;"
"CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;CUT;:H1:7,V;:L(Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;"
"CUT;:H1:7,V);CUT;:H1:3c,E|Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E);CUT;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;CUT;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementBooleanFuse)
@@ -1448,12 +1452,16 @@ TEST_F(TopoShapeExpansionTest, makeElementBooleanFuse)
// Assert element map is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;FUS;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementDraft)
@@ -1707,12 +1715,16 @@ TEST_F(TopoShapeExpansionTest, makeElementGeneralFuse)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 72);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;GFS;:H1:7,F;:U;GFS;:H1:7,E;:L(Face5;:M;GFS;:H1:7,F;:U2;GFS;:H1:8,E|Face5;:M;"
"GFS;:H1:7,F;:U2;GFS;:H1:8,E;:U;GFS;:H1:7,V;:L(Face6;:M;GFS;:H1:7,F;:U2;GFS;:H1:8,E;:U;"
"GFS;:H1:7,V);GFS;:H1:3c,E|Face6;:M;GFS;:H1:7,F;:U2;GFS;:H1:8,E);GFS;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;GFS;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementFuse)
@@ -1732,12 +1744,16 @@ TEST_F(TopoShapeExpansionTest, makeElementFuse)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;FUS;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementCut)
@@ -1758,12 +1774,16 @@ TEST_F(TopoShapeExpansionTest, makeElementCut)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 38);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;CUT;:H1:7,F;:U;CUT;:H1:7,E;:L(Face5;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E|Face5;:M;"
"CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;CUT;:H1:7,V;:L(Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;"
"CUT;:H1:7,V);CUT;:H1:3c,E|Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E);CUT;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;CUT;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementChamfer)
@@ -2170,12 +2190,16 @@ TEST_F(TopoShapeExpansionTest, makeElementTransformWithMap)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;FUS;:H1:4,F"));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementGTransformWithoutMap)
@@ -2216,12 +2240,16 @@ TEST_F(TopoShapeExpansionTest, makeElementGTransformWithMap)
// Assert elementMap is correct
EXPECT_EQ(elements.size(), 66);
EXPECT_EQ(elements.count(IndexedName("Face", 1)), 1);
#ifndef FC_USE_TNP_FIX
EXPECT_EQ(
elements[IndexedName("Face", 1)],
MappedName(
"Face3;:M;FUS;:H1:7,F;:U;FUS;:H1:7,E;:L(Face5;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E|Face5;:M;"
"FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;FUS;:H1:7,V;:L(Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E;:U;"
"FUS;:H1:7,V);FUS;:H1:3c,E|Face6;:M;FUS;:H1:7,F;:U2;FUS;:H1:8,E);FUS;:H1:cb,F"));
#else
EXPECT_EQ(elements[IndexedName("Face", 1)], MappedName("Face1;FUS;:H1:4,F"));
#endif
}
// Not testing _makeElementTransform as it is a thin wrapper that calls the same places as the four
@@ -2594,6 +2622,7 @@ TEST_F(TopoShapeExpansionTest, traceElement)
// Act
result.traceElement(mappedName, cb);
// Assert we have the element map we think we do.
#ifndef FC_USE_TNP_FIX
EXPECT_TRUE(allElementsMatch(
result,
{
@@ -2641,6 +2670,49 @@ TEST_F(TopoShapeExpansionTest, traceElement)
"Face6;:M;CUT;:H1:7,F;:U2;CUT;:H1:8,E;:U;CUT;:H1:7,V",
"Face6;:M;CUT;:H1:7,F;:U;CUT;:H1:7,E",
}));
#else
EXPECT_TRUE(allElementsMatch(result,
{
"Edge10;:G(Edge2;K-1;:H2:4,E);CUT;:H1:1a,V",
"Edge10;:M;CUT;:H1:7,E",
"Edge11;:M;CUT;:H2:7,E",
"Edge11;CUT;:H1:4,E",
"Edge12;:M;CUT;:H2:7,E",
"Edge12;CUT;:H1:4,E",
"Edge1;CUT;:H1:4,E",
"Edge2;:M;CUT;:H2:7,E",
"Edge2;CUT;:H1:4,E",
"Edge3;CUT;:H1:4,E",
"Edge3;CUT;:H2:4,E",
"Edge4;:M;CUT;:H2:7,E",
"Edge4;CUT;:H1:4,E",
"Edge6;:G(Edge12;K-1;:H2:4,E);CUT;:H1:1b,V",
"Edge6;:M;CUT;:H1:7,E",
"Edge7;CUT;:H1:4,E",
"Edge8;:G(Edge11;K-1;:H2:4,E);CUT;:H1:1b,V",
"Edge8;:M;CUT;:H1:7,E",
"Edge9;:G(Edge4;K-1;:H2:4,E);CUT;:H1:1a,V",
"Edge9;:M;CUT;:H1:7,E",
"Face1;:M;CUT;:H2:7,F",
"Face1;CUT;:H1:4,F",
"Face2;:G(Face4;K-1;:H2:4,F);CUT;:H1:1a,E",
"Face2;:M;CUT;:H1:7,F",
"Face3;:G(Face1;K-1;:H2:4,F);CUT;:H1:1a,E",
"Face3;:M;CUT;:H1:7,F",
"Face4;:M;CUT;:H2:7,F",
"Face4;CUT;:H1:4,F",
"Face5;:M;CUT;:H1:7,F",
"Face6;:M;CUT;:H1:7,F",
"Vertex1;CUT;:H1:4,V",
"Vertex2;CUT;:H1:4,V",
"Vertex3;CUT;:H1:4,V",
"Vertex3;CUT;:H2:4,V",
"Vertex4;CUT;:H1:4,V",
"Vertex4;CUT;:H2:4,V",
"Vertex7;CUT;:H1:4,V",
"Vertex8;CUT;:H1:4,V",
}));
#endif
}
TEST_F(TopoShapeExpansionTest, makeElementOffset)