23
#include "PreCompiled.h"
27
# include <Bnd_Box.hxx>
28
# include <BRepAdaptor_Curve.hxx>
29
# include <BRepAlgoAPI_Fuse.hxx>
30
# include <BRepAlgoAPI_Common.hxx>
31
# include <BRepBndLib.hxx>
32
# include <BRepBuilderAPI_MakeEdge.hxx>
33
# include <BRepBuilderAPI_MakeFace.hxx>
34
# include <BRepBuilderAPI_MakeShape.hxx>
35
# include <BRepBuilderAPI_MakeVertex.hxx>
36
# include <BRepExtrema_DistShapeShape.hxx>
37
# include <BRepGProp.hxx>
38
# include <BRepGProp_Face.hxx>
39
# include <BRepIntCurveSurface_Inter.hxx>
40
# include <gce_MakeDir.hxx>
41
# include <gce_MakeLin.hxx>
45
# include <gp_Trsf.hxx>
46
# include <GProp_GProps.hxx>
47
# include <IntCurveSurface_IntersectionPoint.hxx>
48
# include <Precision.hxx>
49
# include <Standard_Failure.hxx>
50
# include <Standard_Version.hxx>
52
# include <TopExp_Explorer.hxx>
54
# include <TopTools_IndexedMapOfShape.hxx>
55
# include <TopTools_ListIteratorOfListOfShape.hxx>
58
#include <App/Application.h>
59
#include <App/Document.h>
60
#include <App/FeaturePythonPyImp.h>
61
#include <App/GeoFeature.h>
63
#include <App/GeoFeatureGroupExtension.h>
64
#include <App/ElementNamingUtils.h>
65
#include <App/Placement.h>
66
#include <App/OriginFeature.h>
67
#include <Base/Exception.h>
68
#include <Base/Placement.h>
69
#include <Base/Rotation.h>
70
#include <Base/Stream.h>
71
#include <Mod/Material/App/MaterialManager.h>
74
#include "PartFeature.h"
75
#include "PartFeaturePy.h"
77
#include "TopoShapePy.h"
78
#include "Base/Tools.h"
81
namespace sp = std::placeholders;
83
FC_LOG_LEVEL_INIT("Part",true,true)
85
PROPERTY_SOURCE(Part::Feature, App::GeoFeature)
90
ADD_PROPERTY(Shape, (TopoDS_Shape()));
91
auto mat = Materials::MaterialManager::defaultMaterial();
93
ADD_PROPERTY(ShapeMaterial, (*mat));
96
Feature::~Feature() = default;
98
short Feature::mustExecute() const
100
return GeoFeature::mustExecute();
103
App::DocumentObjectExecReturn *Feature::recompute()
106
return App::GeoFeature::recompute();
108
catch (Standard_Failure& e) {
110
App::DocumentObjectExecReturn* ret = new App::DocumentObjectExecReturn(e.GetMessageString());
111
if (ret->Why.empty()) ret->Why = "Unknown OCC exception";
116
App::DocumentObjectExecReturn *Feature::execute()
119
return GeoFeature::execute();
122
PyObject *Feature::getPyObject()
124
if (PythonObject.is(Py::_None())){
126
PythonObject = Py::Object(new PartFeaturePy(this),true);
128
return Py::new_reference_to(PythonObject);
138
App::ElementNamePair Feature::getElementName(const char* name,
139
ElementNameType type) const
141
if (type != ElementNameType::Export) {
142
return App::GeoFeature::getElementName(name, type);
148
auto prop = Base::freecad_dynamic_cast<PropertyPartShape>(getPropertyOfGeometry());
150
return App::GeoFeature::getElementName(name, type);
152
return getExportElementName(prop->getShape(), name);
155
App::ElementNamePair Feature::getExportElementName(TopoShape shape,
156
const char* name) const
158
Data::MappedElement mapped = shape.getElementName(name);
159
auto res = shape.shapeTypeAndIndex(mapped.index);
160
static const int MinLowerTopoNames = 3;
161
static const int MaxLowerTopoNames = 10;
162
if (res.second && !mapped.name) {
180
auto subshape = shape.getSubTopoShape(res.first, res.second, true);
181
TopAbs_ShapeEnum lower;
182
Data::IndexedName idxName;
183
if (!subshape.isNull()) {
187
idxName = Data::IndexedName::fromConst("Edge", 1);
191
case TopAbs_COMPOUND:
192
case TopAbs_COMPSOLID:
194
idxName = Data::IndexedName::fromConst("Face", 1);
197
lower = TopAbs_SHAPE;
199
if (lower != TopAbs_SHAPE) {
200
typedef std::pair<size_t, std::vector<int>> NameEntry;
201
std::vector<NameEntry> indices;
202
std::vector<Data::MappedName> names;
203
std::vector<int> ancestors;
205
for (auto& ss : subshape.getSubTopoShapes(lower)) {
206
auto name = ss.getMappedName(idxName);
210
indices.emplace_back(name.size(),
211
shape.findAncestors(ss.getShape(), res.first));
212
names.push_back(name);
213
if (indices.back().second.size() == 1 && ++count >= MinLowerTopoNames) {
218
if (names.size() >= MaxLowerTopoNames) {
219
std::stable_sort(indices.begin(),
221
[](const NameEntry& a, const NameEntry& b) {
222
return a.second.size() < b.second.size();
224
std::vector<Data::MappedName> sorted;
226
sorted.reserve(names.size());
227
for (auto& v : indices) {
228
size_t size = ancestors.size();
230
ancestors = v.second;
233
for (auto it = ancestors.begin(); it != ancestors.end();) {
234
if (std::find(v.second.begin(), v.second.end(), *it)
236
it = ancestors.erase(it);
237
if (ancestors.size() == 1) {
246
auto itPos = sorted.end();
247
if (size == 1 || size != ancestors.size()) {
248
itPos = sorted.begin() + pos;
251
sorted.insert(itPos, names[v.first]);
252
if (size == 1 && sorted.size() >= MinLowerTopoNames) {
258
names.resize(std::min((int)names.size(), MaxLowerTopoNames));
261
if (ancestors.size() > 1) {
265
auto it = std::find(ancestors.begin(), ancestors.end(), res.second);
266
if (it == ancestors.end()) {
267
assert(0 && "ancestor not found");
270
op = Data::POSTFIX_INDEX + std::to_string(it - ancestors.begin());
285
mapped.name = shape.setElementComboName(mapped.index,
287
mapped.index.getType(),
293
else if (!res.second && mapped.name) {
294
const char* dot = strchr(name, '.');
305
if (Data::hasMissingElement(dot)) {
306
dot += strlen(Data::MISSING_PREFIX);
308
std::pair<TopAbs_ShapeEnum, int> occindex = shape.shapeTypeAndIndex(dot);
309
if (occindex.second > 0) {
310
auto idxName = Data::IndexedName::fromConst(shape.shapeName(occindex.first).c_str(),
314
shape.decodeElementComboName(idxName, mapped.name, idxName.getType(), &postfix);
315
std::vector<int> ancestors;
319
for (auto& name : names) {
320
auto index = shape.getIndexedName(name);
325
auto oidx = shape.shapeTypeAndIndex(index);
326
auto subshape = shape.getSubShape(oidx.first, oidx.second);
327
if (subshape.IsNull()) {
331
auto current = shape.findAncestors(subshape, occindex.first);
332
if (ancestors.empty()) {
333
ancestors = std::move(current);
336
for (auto it = ancestors.begin(); it != ancestors.end();) {
337
if (std::find(current.begin(), current.end(), *it) == current.end()) {
338
it = ancestors.erase(it);
344
if (ancestors.empty()) {
349
if (ancestors.size() > 1 && boost::starts_with(postfix, Data::POSTFIX_INDEX)) {
350
std::istringstream iss(postfix.c_str() + strlen(Data::POSTFIX_INDEX));
352
if (iss >> idx && idx >= 0 && idx < (int)ancestors.size()) {
353
ancestors.resize(1, ancestors[idx]);
356
if (ancestors.size() == 1) {
357
idxName.setIndex(ancestors.front());
358
mapped.index = idxName;
363
return App::GeoFeature::_getElementName(name, mapped);
366
App::DocumentObject* Feature::getSubObject(const char* subname,
368
Base::Matrix4D* pmat,
372
while(subname && *subname=='.') ++subname;
376
if (subname && !Data::isMappedElement(subname) && strchr(subname, '.')) {
377
return App::DocumentObject::getSubObject(subname, pyObj, pmat, transform, depth);
381
auto& mat = pmat ? *pmat : _mat;
383
mat *= Placement.getValue().toMatrix();
388
return const_cast<Feature*>(this);
392
TopoShape ts(Shape.getShape());
393
bool doTransform = mat != ts.getTransform();
395
ts.setShape(ts.getShape().Located(TopLoc_Location()), false);
397
if (subname && *subname && !ts.isNull()) {
398
ts = ts.getSubTopoShape(subname,true);
400
if (doTransform && !ts.isNull()) {
401
static int sCopy = -1;
403
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
404
"User parameter:BaseApp/Preferences/Mod/Part/General");
405
sCopy = hGrp->GetBool("CopySubShape", false) ? 1 : 0;
407
bool copy = sCopy ? true : false;
412
TopExp_Explorer exp(ts.getShape(), TopAbs_EDGE);
414
auto edge = TopoDS::Edge(exp.Current());
417
BRepAdaptor_Curve curve(edge);
418
copy = curve.GetType() == GeomAbs_Circle;
422
ts.transformShape(mat, copy, true);
424
*pyObj = Py::new_reference_to(shape2pyshape(ts));
425
return const_cast<Feature*>(this);
427
catch (Standard_Failure& e) {
433
std::ostringstream str;
434
Standard_CString msg = e.GetMessageString();
437
str << e.DynamicType()->get_type_name() << " ";
443
str << "No OCCT Exception Message";
445
str << ": " << getFullName();
447
str << '.' << subname;
454
static std::vector<std::pair<long, Data::MappedName>> getElementSource(App::DocumentObject* owner,
456
const Data::MappedName& name,
459
std::set<std::pair<App::Document*, long>> tagSet;
460
std::vector<std::pair<long, Data::MappedName>> ret;
461
ret.emplace_back(0, name);
464
Data::MappedName original;
465
std::vector<Data::MappedName> history;
469
if (!shape.Hasher && owner) {
470
shape.Hasher = owner->getDocument()->getStringHasher();
472
long tag = shape.getElementHistory(ret.back().second, &original, &history);
477
App::Document* doc = nullptr;
479
doc = owner->getDocument();
481
auto linked = owner->getLinkedObject(false, nullptr, false, depth);
482
if (linked == owner) {
486
if (owner->getDocument() != doc) {
487
doc = owner->getDocument();
491
if (owner->isDerivedFrom(App::GeoFeature::getClassTypeId())) {
492
auto ownerGeoFeature =
493
static_cast<App::GeoFeature*>(owner)->getElementOwner(ret.back().second);
494
if (ownerGeoFeature) {
495
doc = ownerGeoFeature->getDocument();
498
obj = doc->getObjectByID(tag < 0 ? -tag : tag);
500
for (auto& hist : history) {
501
if (shape.elementType(hist) != type) {
511
shape.setShape(TopoDS_Shape());
515
shape = Part::Feature::getTopoShape(obj, 0, false, 0, &owner);
517
if (type && shape.elementType(original) != type) {
521
if (std::abs(tag) != ret.back().first && !tagSet.insert(std::make_pair(doc, tag)).second) {
526
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG)) {
527
FC_WARN("circular element mapping");
531
ret.emplace_back(tag, original);
536
std::list<Data::HistoryItem> Feature::getElementHistory(App::DocumentObject* feature,
541
std::list<Data::HistoryItem> ret;
542
TopoShape shape = getTopoShape(feature);
543
Data::IndexedName idx(name);
544
Data::MappedName element;
545
Data::MappedName prevElement;
547
element = shape.getMappedName(idx, true);
549
else if (Data::isMappedElement(name)) {
550
element = Data::MappedName(Data::newElementName(name));
553
element = Data::MappedName(name);
555
char element_type = 0;
557
element_type = shape.elementType(element);
561
Data::MappedName original;
562
ret.emplace_back(feature, element);
563
long tag = shape.getElementHistory(element, &original, &ret.back().intermediates);
565
ret.back().index = shape.getIndexedName(element);
566
if (!ret.back().index && prevElement) {
567
ret.back().index = shape.getIndexedName(prevElement);
568
if (ret.back().index) {
569
ret.back().intermediates.insert(ret.back().intermediates.begin(), element);
570
ret.back().element = prevElement;
573
if (ret.back().intermediates.size()) {
574
prevElement = ret.back().intermediates.back();
577
prevElement = Data::MappedName();
580
App::DocumentObject* obj = nullptr;
582
App::Document* doc = feature->getDocument();
584
auto linked = feature->getLinkedObject(false, nullptr, false, depth);
585
if (linked == feature) {
589
if (feature->getDocument() != doc) {
590
doc = feature->getDocument();
594
if (feature->isDerivedFrom(App::GeoFeature::getClassTypeId())) {
595
auto ownerGeoFeature =
596
static_cast<App::GeoFeature*>(feature)->getElementOwner(element);
597
if (ownerGeoFeature) {
598
doc = ownerGeoFeature->getDocument();
601
obj = doc->getObjectByID(std::abs(tag));
604
ret.emplace_back(obj, original);
605
ret.back().tag = tag;
612
for (auto& hist : ret.back().intermediates) {
613
if (shape.elementType(hist) != element_type) {
619
shape = Feature::getTopoShape(feature);
621
if (element_type && shape.elementType(original) != element_type) {
628
QVector<Data::MappedElement> Feature::getElementFromSource(App::DocumentObject* obj,
630
App::DocumentObject* src,
634
QVector<Data::MappedElement> res;
639
auto shape = getTopoShape(obj,
646
App::DocumentObject* owner = nullptr;
647
auto srcShape = getTopoShape(src, srcSub, false, nullptr, &owner);
649
Data::MappedElement element;
650
Data::IndexedName checkingSubname;
651
std::string sub = Data::noElementName(subname);
652
auto checkHistory = [&](const Data::MappedName& name, size_t, long, long tag) {
653
if (std::abs(tag) == owner->getID()) {
658
else if (tagChanges && ++tagChanges > 3) {
663
if (name == element.name) {
664
App::ElementNamePair objElement;
665
std::size_t len = sub.size();
666
checkingSubname.appendToStringBuffer(sub);
667
GeoFeature::resolveElement(obj, sub.c_str(), objElement);
669
if (objElement.oldName.size()) {
670
res.push_back(Data::MappedElement(Data::MappedName(objElement.newName),
671
Data::IndexedName(objElement.oldName.c_str())));
679
App::ElementNamePair objElement;
680
GeoFeature::resolveElement(src, srcSub, objElement, false);
682
element.index = Data::IndexedName(objElement.oldName.c_str());
683
if (!objElement.newName.empty()) {
685
auto mappedName = Data::newElementName(objElement.newName.c_str());
686
auto mapped = Data::isMappedElement(mappedName.c_str());
688
element.name = Data::MappedName(mapped);
693
if (objElement.oldName == "Plane") {
694
objElement.oldName = "Face1";
696
else if (objElement.oldName == "Line") {
697
objElement.oldName = "Edge1";
699
else if (objElement.oldName == "Point") {
700
objElement.oldName = "Vertex1";
704
auto type = TopoShape::shapeType(Data::findElementName(objElement.oldName.c_str()));
709
if (type != TopAbs_SHAPE && element.name
710
&& shape.countSubShapes(type) == srcShape.countSubShapes(type)) {
712
checkingSubname = element.index;
713
auto mapped = shape.getMappedName(element.index);
714
shape.traceElement(mapped, checkHistory);
721
auto subShape = srcShape.getSubShape(objElement.oldName.c_str());
722
std::vector<std::string> names;
723
shape.findSubShapesWithSharedVertex(subShape, &names);
725
for (auto& name : names) {
726
Data::MappedElement e;
727
e.index = Data::IndexedName(name.c_str());
728
e.name = shape.getMappedName(e.index, true);
737
if (!element.name || type == TopAbs_SHAPE) {
744
const char* shapetype = TopoShape::shapeName(type).c_str();
745
for (int i = 0, count = shape.countSubShapes(type); i < count; ++i) {
746
checkingSubname = Data::IndexedName::fromConst(shapetype, i + 1);
747
auto mapped = shape.getMappedName(checkingSubname);
749
shape.traceElement(mapped, checkHistory);
750
if (single && res.size()) {
757
QVector<Data::MappedElement> Feature::getRelatedElements(App::DocumentObject* obj,
759
HistoryTraceType sameType,
763
auto shape = getTopoShape(obj, nullptr, false, 0, &owner);
764
QVector<Data::MappedElement> ret;
765
Data::MappedElement mapped = shape.getElementName(name);
769
if (withCache && shape.getRelatedElementsCached(mapped.name, sameType, ret)) {
773
char element_type = shape.elementType(mapped.name);
774
TopAbs_ShapeEnum type = TopoShape::shapeType(element_type, true);
775
if (type == TopAbs_SHAPE) {
780
getElementSource(owner,
783
sameType == HistoryTraceType::followTypeChange ? element_type : 0);
784
for (auto& src : source) {
785
auto srcIndex = shape.getIndexedName(src.second);
787
ret.push_back(Data::MappedElement(src.second, srcIndex));
788
shape.cacheRelatedElements(mapped.name, sameType, ret);
793
std::map<int, QVector<Data::MappedElement>> retMap;
795
const char* shapetype = TopoShape::shapeName(type).c_str();
796
std::ostringstream ss;
797
for (size_t i = 1; i <= shape.countSubShapes(type); ++i) {
798
Data::MappedElement related;
799
related.index = Data::IndexedName::fromConst(shapetype, i);
800
related.name = shape.getMappedName(related.index);
805
getElementSource(owner,
808
sameType == HistoryTraceType::followTypeChange ? element_type : 0);
809
int idx = (int)source.size() - 1;
810
for (auto rit = src.rbegin(); idx >= 0 && rit != src.rend(); ++rit, --idx) {
814
if (rit->second != source[idx].second) {
819
if (idx < (int)source.size()) {
820
retMap[idx].push_back(related);
824
ret = retMap.begin()->second;
826
shape.cacheRelatedElements(mapped.name, sameType, ret);
830
TopoDS_Shape Feature::getShape(const App::DocumentObject *obj, const char *subname,
831
bool needSubElement, Base::Matrix4D *pmat, App::DocumentObject **powner,
832
bool resolveLink, bool transform)
834
return getTopoShape(obj,subname,needSubElement,pmat,powner,resolveLink,transform,true).getShape();
837
App::Material Feature::getMaterialAppearance() const
839
return ShapeMaterial.getValue().getMaterialAppearance();
842
void Feature::setMaterialAppearance(const App::Material& material)
844
ShapeMaterial.setValue(material);
848
void Feature::clearShapeCache() {
852
static TopoShape _getTopoShape(const App::DocumentObject* obj,
855
Base::Matrix4D* pmat,
856
App::DocumentObject** powner,
859
const std::set<std::string> hiddens,
860
const App::DocumentObject* lastLink)
869
PyObject* pyobj = nullptr;
875
std::string _subname;
876
auto subelement = Data::findElementName(subname);
877
if (!needSubElement && subname) {
879
if (subelement && *subelement) {
880
_subname = std::string(subname, subelement);
881
subname = _subname.c_str();
885
auto canCache = [&](const App::DocumentObject* o) {
886
return !lastLink || (hiddens.empty() && !App::GeoFeatureGroupExtension::isNonGeoGroup(o));
889
if (canCache(obj) && PropertyShapeCache::getShape(obj, shape, subname)) {
891
shape.resetElementMap();
893
if ( shape.Hasher ) {
894
shape.Hasher = nullptr;
899
App::DocumentObject* linked = nullptr;
900
App::DocumentObject* owner = nullptr;
901
Base::Matrix4D linkMat;
902
App::StringHasherRef hasher;
905
Base::PyGILStateLocker lock;
906
owner = obj->getSubObject(subname, shape.isNull() ? &pyobj : nullptr, &mat, false);
910
tag = owner->getID();
911
hasher = owner->getDocument()->getStringHasher();
912
linked = owner->getLinkedObject(true, &linkMat, false);
914
if (resolveLink && obj != owner) {
915
*pmat = mat * linkMat;
925
*powner = resolveLink ? linked : owner;
928
if (!shape.isNull()) {
932
if (pyobj && PyObject_TypeCheck(pyobj, &TopoShapePy::Type)) {
933
shape = *static_cast<TopoShapePy*>(pyobj)->getTopoShapePtr();
934
if (!shape.isNull()) {
936
if (obj->getDocument() != linked->getDocument()
937
|| mat.hasScale() != Base::ScaleType::NoScaling
938
|| (linked != owner && linkMat.hasScale() != Base::ScaleType::NoScaling)) {
939
PropertyShapeCache::setShape(obj, shape, subname);
943
shape.resetElementMap();
945
if ( shape.Hasher ) {
946
shape.Hasher = nullptr;
954
if (linked->isDerivedFrom(App::Line::getClassTypeId())) {
955
static TopoDS_Shape _shape;
956
if (_shape.IsNull()) {
957
BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)));
958
_shape = builder.Shape();
959
_shape.Infinite(Standard_True);
961
shape = TopoShape(tag, hasher, _shape);
963
else if (linked->isDerivedFrom(App::Plane::getClassTypeId())) {
964
static TopoDS_Shape _shape;
965
if (_shape.IsNull()) {
966
BRepBuilderAPI_MakeFace builder(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)));
967
_shape = builder.Shape();
968
_shape.Infinite(Standard_True);
970
shape = TopoShape(tag, hasher, _shape);
972
else if (linked->isDerivedFrom(App::Placement::getClassTypeId())) {
973
auto element = Data::findElementName(subname);
975
if (boost::iequals("x", element) || boost::iequals("x-axis", element)
976
|| boost::iequals("y", element) || boost::iequals("y-axis", element)
977
|| boost::iequals("z", element) || boost::iequals("z-axis", element)) {
978
static TopoDS_Shape _shape;
979
if (_shape.IsNull()) {
980
BRepBuilderAPI_MakeEdge builder(
981
gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)));
982
_shape = builder.Shape();
983
_shape.Infinite(Standard_True);
985
shape = TopoShape(tag, hasher, _shape);
987
else if (boost::iequals("o", element) || boost::iequals("origin", element)) {
988
static TopoDS_Shape _shape;
989
if (_shape.IsNull()) {
990
BRepBuilderAPI_MakeVertex builder(gp_Pnt(0, 0, 0));
991
_shape = builder.Shape();
992
_shape.Infinite(Standard_True);
994
shape = TopoShape(tag, hasher, _shape);
997
if (shape.isNull()) {
998
static TopoDS_Shape _shape;
999
if (_shape.IsNull()) {
1000
BRepBuilderAPI_MakeFace builder(gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)));
1001
_shape = builder.Shape();
1002
_shape.Infinite(Standard_True);
1004
shape = TopoShape(tag, hasher, _shape);
1007
if (!shape.isNull()) {
1008
shape.transformShape(mat * linkMat, false, true);
1017
if (needSubElement && subelement && *subelement) {
1022
if (canCache(owner) && PropertyShapeCache::getShape(owner, shape)) {
1023
bool scaled = shape.transformShape(mat, false, true);
1024
if (owner->getDocument() != obj->getDocument()) {
1025
shape.reTagElementMap(obj->getID(), obj->getDocument()->getStringHasher());
1026
PropertyShapeCache::setShape(obj, shape, subname);
1029
|| (linked != owner && linkMat.hasScale() != Base::ScaleType::NoScaling)) {
1030
PropertyShapeCache::setShape(obj, shape, subname);
1033
if (!shape.isNull()) {
1035
shape.resetElementMap();
1037
if ( shape.Hasher) {
1038
shape.Hasher = nullptr;
1045
bool cacheable = true;
1047
auto link = owner->getExtensionByType<App::LinkBaseExtension>(true);
1049
&& (!link || (!link->_ChildCache.getSize() && link->getSubElements().size() <= 1))) {
1052
shape = Feature::getTopoShape(linked, nullptr, false, nullptr, nullptr, false, false);
1053
if (shape.isNull()) {
1057
shape.transformShape(mat * linkMat, false, true);
1060
shape.transformShape(linkMat, false, true);
1062
shape.reTagElementMap(tag, hasher);
1066
std::vector<TopoShape> shapes;
1071
TopoShape baseShape;
1072
Base::Matrix4D baseMat;
1074
if (link && link->getElementCountValue()) {
1075
linked = link->getTrueLinkedObject(false, &baseMat);
1076
if (linked && linked != owner) {
1078
Feature::getTopoShape(linked, nullptr, false, nullptr, nullptr, false, false);
1079
if (!link->getShowElementValue()) {
1080
baseShape.reTagElementMap(owner->getID(),
1081
owner->getDocument()->getStringHasher());
1085
for (auto& sub : owner->getSubObjects()) {
1090
std::string childName;
1091
App::DocumentObject* parent = nullptr;
1092
Base::Matrix4D mat = baseMat;
1093
App::DocumentObject* subObj = nullptr;
1094
if (sub.find('.') == std::string::npos) {
1099
owner->resolve(sub.c_str(), &parent, &childName, nullptr, nullptr, &mat, false);
1100
if (!parent || !subObj) {
1103
if (lastLink && App::GeoFeatureGroupExtension::isNonGeoGroup(parent)) {
1104
visible = lastLink->isElementVisible(childName.c_str());
1107
visible = parent->isElementVisible(childName.c_str());
1114
std::set<std::string> nextHiddens = hiddens;
1115
const App::DocumentObject* nextLink = lastLink;
1125
bool doGetShape = (!subObj || baseShape.isNull());
1127
auto type = mat.hasScale();
1128
if (type != Base::ScaleType::NoScaling && type != Base::ScaleType::Uniform) {
1133
shape = _getTopoShape(owner,
1142
if (shape.isNull()) {
1145
if (visible < 0 && subObj && !subObj->Visibility.getValue()) {
1150
if (link && !link->getShowElementValue()) {
1152
baseShape.makeElementTransform(mat,
1153
(Data::POSTFIX_INDEX + childName).c_str());
1156
shape = baseShape.makeElementTransform(mat);
1157
shape.reTagElementMap(subObj->getID(),
1158
subObj->getDocument()->getStringHasher());
1161
shapes.push_back(shape);
1164
if (shapes.empty()) {
1168
shape.Hasher = hasher;
1169
shape.makeElementCompound(shapes);
1172
if (cacheable && canCache(owner)) {
1173
PropertyShapeCache::setShape(owner, shape);
1177
bool scaled = shape.transformShape(mat, false, true);
1178
if (owner->getDocument() != obj->getDocument()) {
1179
shape.reTagElementMap(obj->getID(), obj->getDocument()->getStringHasher());
1182
if (canCache(obj) && scaled) {
1183
PropertyShapeCache::setShape(obj, shape, subname);
1187
shape.resetElementMap();
1189
if ( shape.Hasher ) {
1190
shape.Hasher = nullptr;
1196
TopoShape Feature::getTopoShape(const App::DocumentObject* obj,
1197
const char* subname,
1198
bool needSubElement,
1199
Base::Matrix4D* pmat,
1200
App::DocumentObject** powner,
1205
if (!obj || !obj->getNameInDocument()) {
1209
const App::DocumentObject* lastLink = 0;
1210
std::set<std::string> hiddens;
1221
if (needSubElement && (!pmat || *pmat == Base::Matrix4D())
1222
&& obj->isDerivedFrom(Part::Feature::getClassTypeId())
1223
&& !obj->hasExtension(App::LinkBaseExtension::getExtensionClassTypeId())) {
1227
if (subname && *subname && Data::findElementName(subname) == subname) {
1228
TopoShape ts = static_cast<const Part::Feature*>(obj)->Shape.getShape();
1230
ts.setShape(ts.getShape().Located(TopLoc_Location()), false);
1233
ts = ts.getSubShape(subname, true);
1236
ts = ts.getSubTopoShape(subname, true);
1240
*powner = const_cast<App::DocumentObject*>(obj);
1242
if (pmat && transform) {
1243
*pmat = static_cast<const Part::Feature*>(obj)->Placement.getValue().toMatrix();
1251
auto shape = _getTopoShape(obj,
1260
if (needSubElement && shape.shapeType(true) == TopAbs_COMPOUND) {
1261
if (shape.countSubShapes(TopAbs_SOLID) == 1)
1262
shape = shape.getSubTopoShape(TopAbs_SOLID, 1);
1263
else if (shape.countSubShapes(TopAbs_COMPSOLID) == 1)
1264
shape = shape.getSubTopoShape(TopAbs_COMPSOLID, 1);
1265
else if (shape.countSubShapes(TopAbs_FACE) == 1)
1266
shape = shape.getSubTopoShape(TopAbs_FACE, 1);
1267
else if (shape.countSubShapes(TopAbs_SHELL) == 1)
1268
shape = shape.getSubTopoShape(TopAbs_SHELL, 1);
1269
else if (shape.countSubShapes(TopAbs_EDGE) == 1)
1270
shape = shape.getSubTopoShape(TopAbs_EDGE, 1);
1271
else if (shape.countSubShapes(TopAbs_WIRE) == 1)
1272
shape = shape.getSubTopoShape(TopAbs_WIRE, 1);
1273
else if (shape.countSubShapes(TopAbs_VERTEX) == 1)
1274
shape = shape.getSubTopoShape(TopAbs_VERTEX, 1);
1276
Base::Matrix4D topMat;
1277
if (pmat || transform) {
1283
obj->getSubObject(nullptr, nullptr, &topMat);
1287
if (!shape.isNull()) {
1288
shape.transformShape(topMat, false, true);
1292
*pmat = topMat * mat;
1299
App::DocumentObject *Feature::getShapeOwner(const App::DocumentObject *obj, const char *subname)
1303
auto owner = obj->getSubObject(subname);
1305
auto linked = owner->getLinkedObject(true);
1312
struct Feature::ElementCache
1315
mutable std::vector<std::string> names;
1316
mutable bool searched;
1319
void Feature::registerElementCache(const std::string& prefix, PropertyPartShape* prop)
1322
_elementCachePrefixMap.emplace_back(prefix, prop);
1325
for (auto it = _elementCachePrefixMap.begin(); it != _elementCachePrefixMap.end();) {
1326
if (it->first == prefix) {
1327
_elementCachePrefixMap.erase(it);
1333
void Feature::onBeforeChange(const App::Property* prop)
1335
PropertyPartShape* propShape = nullptr;
1336
const std::string* prefix = nullptr;
1337
if (prop == &Shape) {
1341
for (const auto& v : _elementCachePrefixMap) {
1342
if (prop == v.second) {
1344
propShape = v.second;
1349
if (_elementCachePrefixMap.empty()) {
1350
_elementCache.clear();
1353
for (auto it = _elementCache.begin(); it != _elementCache.end();) {
1356
remove = boost::starts_with(it->first, *prefix);
1360
for (const auto& v : _elementCache) {
1361
if (boost::starts_with(it->first, v.first)) {
1368
it = _elementCache.erase(it);
1375
if (getDocument() && !getDocument()->testStatus(App::Document::Restoring)
1376
&& !getDocument()->isPerformingTransaction()) {
1377
std::vector<App::DocumentObject*> objs;
1378
std::vector<std::string> subs;
1379
for (auto prop : App::PropertyLinkBase::getElementReferences(this)) {
1380
if (!prop->getContainer()) {
1385
prop->getLinks(objs, true, &subs, false);
1386
for (auto& sub : subs) {
1387
auto element = Data::findElementName(sub.c_str());
1388
if (!element || !element[0] || Data::hasMissingElement(element)) {
1392
if (!boost::starts_with(element, *prefix)) {
1398
for (const auto& v : _elementCachePrefixMap) {
1399
if (boost::starts_with(element, v.first)) {
1409
_elementCache.insert(std::make_pair(std::string(element), ElementCache()));
1411
res.first->second.searched = false;
1412
res.first->second.shape = propShape->getShape().getSubTopoShape(
1413
element + (prefix ? prefix->size() : 0),
1420
GeoFeature::onBeforeChange(prop);
1423
void Feature::onChanged(const App::Property* prop)
1426
if (prop == &this->Placement) {
1427
TopoShape shape = this->Shape.getShape();
1428
auto oldTransform = shape.getTransform();
1429
auto newTransform = this->Placement.getValue().toMatrix();
1430
shape.setTransform(newTransform);
1431
Base::ObjectStatusLocker<App::Property::Status, App::Property> guard(
1432
App::Property::NoRecompute,
1434
if ( oldTransform != newTransform) {
1435
this->Shape.setValue(shape);
1439
else if (prop == &this->Shape) {
1440
if (this->isRecomputing()) {
1441
this->Shape._Shape.setTransform(this->Placement.getValue().toMatrix());
1446
if (!this->Shape.getValue().IsNull()) {
1448
p.fromMatrix(this->Shape.getShape().getTransform());
1449
this->Placement.setValueIfChanged(p);
1451
catch (const Base::ValueError&) {
1457
GeoFeature::onChanged(prop);
1461
const std::vector<std::string>& Feature::searchElementCache(const std::string& element,
1462
Data::SearchOptions options,
1466
static std::vector<std::string> none;
1467
if (element.empty()) {
1470
auto it = _elementCache.find(element);
1471
if (it == _elementCache.end() || it->second.shape.isNull()) {
1474
if (!it->second.searched) {
1475
auto propShape = &Shape;
1476
const std::string* prefix = nullptr;
1477
for (const auto& v : _elementCachePrefixMap) {
1478
if (boost::starts_with(element, v.first)) {
1479
propShape = v.second;
1484
it->second.searched = true;
1485
propShape->getShape().findSubShapesWithSharedVertex(it->second.shape,
1491
for (auto& name : it->second.names) {
1492
if (auto dot = strrchr(name.c_str(), '.')) {
1493
name.insert(dot + 1 - name.c_str(), *prefix);
1496
name.insert(0, *prefix);
1501
return it->second.names;
1504
TopLoc_Location Feature::getLocation() const
1506
Base::Placement pl = this->Placement.getValue();
1507
Base::Rotation rot(pl.getRotation());
1508
Base::Vector3d axis;
1510
rot.getValue(axis, angle);
1512
trf.SetRotation(gp_Ax1(gp_Pnt(), gp_Dir(axis.x, axis.y, axis.z)), angle);
1513
trf.SetTranslationPart(gp_Vec(pl.getPosition().x,pl.getPosition().y,pl.getPosition().z));
1514
return TopLoc_Location(trf);
1517
Feature* Feature::create(const TopoShape& shape, const char* name, App::Document* document)
1519
if (!name || !name[0]) {
1523
document = App::GetApplication().getActiveDocument();
1525
document = App::GetApplication().newDocument();
1528
auto res = static_cast<Part::Feature*>(document->addObject("Part::Feature", name));
1529
res->Shape.setValue(shape);
1530
res->purgeTouched();
1534
ShapeHistory Feature::buildHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
1535
const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
1537
ShapeHistory history;
1538
history.type = type;
1540
TopTools_IndexedMapOfShape newM, oldM;
1541
TopExp::MapShapes(newS, type, newM);
1542
TopExp::MapShapes(oldS, type, oldM);
1545
for (int i=1; i<=oldM.Extent(); i++) {
1547
TopTools_ListIteratorOfListOfShape it;
1549
for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) {
1551
for (int j=1; j<=newM.Extent(); j++) {
1552
if (newM(j).IsPartner(it.Value())) {
1553
history.shapeMap[i-1].push_back(j-1);
1560
for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) {
1562
for (int j=1; j<=newM.Extent(); j++) {
1563
if (newM(j).IsPartner(it.Value())) {
1564
history.shapeMap[i-1].push_back(j-1);
1572
if (mkShape.IsDeleted(oldM(i))) {
1573
history.shapeMap[i-1] = std::vector<int>();
1577
for (int j=1; j<=newM.Extent(); j++) {
1578
if (newM(j).IsPartner(oldM(i))) {
1579
history.shapeMap[i-1].push_back(j-1);
1590
ShapeHistory Feature::joinHistory(const ShapeHistory& oldH, const ShapeHistory& newH)
1593
join.type = oldH.type;
1595
for (const auto & it : oldH.shapeMap) {
1596
int old_shape_index = it.first;
1597
if (it.second.empty())
1598
join.shapeMap[old_shape_index] = ShapeHistory::List();
1599
for (const auto& jt : it.second) {
1600
const auto& kt = newH.shapeMap.find(jt);
1601
if (kt != newH.shapeMap.end()) {
1602
ShapeHistory::List& ary = join.shapeMap[old_shape_index];
1603
ary.insert(ary.end(), kt->second.begin(), kt->second.end());
1612
const char* Feature::getViewProviderName() const {
1613
return "PartGui::ViewProviderPart";
1616
const App::PropertyComplexGeoData* Feature::getPropertyOfGeometry() const
1621
bool Feature::isElementMappingDisabled(App::PropertyContainer* container)
1644
bool Feature::getCameraAlignmentDirection(Base::Vector3d& direction, const char* subname) const
1646
const auto topoShape = getTopoShape(this, subname, true);
1648
if (topoShape.isNull()) {
1653
if (topoShape.isPlanar()) {
1655
const auto face = TopoDS::Face(topoShape.getShape());
1658
BRepGProp_Face(face).Normal(0, 0, point, vector);
1659
direction = Base::Vector3d(vector.X(), vector.Y(), vector.Z()).Normalize();
1662
catch (Standard_TypeMismatch&) {
1668
const size_t edgeCount = topoShape.countSubShapes(TopAbs_EDGE);
1669
if (edgeCount == 1 && topoShape.isLinearEdge()) {
1670
if (const std::unique_ptr<Geometry> geometry = Geometry::fromShape(topoShape.getSubShape(TopAbs_EDGE, 1), true)) {
1671
const std::unique_ptr<GeomLine> geomLine(static_cast<GeomCurve*>(geometry.get())->toLine());
1673
direction = geomLine->getDir().Normalize();
1679
return GeoFeature::getCameraAlignmentDirection(direction, subname);
1682
void Feature::guessNewLink(std::string &replacementName, DocumentObject *base, const char *oldLink) {
1683
for (auto &element : Part::Feature::getRelatedElements(base, oldLink)) {
1684
replacementName.clear();
1685
element.index.appendToStringBuffer(replacementName);
1686
FC_WARN("Feature guess element reference " << oldLink << " -> " << replacementName);
1689
replacementName = oldLink;
1694
PROPERTY_SOURCE(Part::FilletBase, Part::Feature)
1696
FilletBase::FilletBase()
1698
ADD_PROPERTY(Base,(nullptr));
1699
ADD_PROPERTY(Edges,(0,0,0));
1700
ADD_PROPERTY_TYPE(EdgeLinks,(0), 0,
1701
(App::PropertyType)(App::Prop_ReadOnly|App::Prop_Hidden),0);
1705
short FilletBase::mustExecute() const
1707
if (Base.isTouched() || Edges.isTouched() || EdgeLinks.isTouched())
1712
void FilletBase::onChanged(const App::Property *prop) {
1713
if(getDocument() && !getDocument()->testStatus(App::Document::Restoring)) {
1714
if(prop == &Edges || prop == &Base) {
1715
if(!prop->testStatus(App::Property::User3))
1719
Feature::onChanged(prop);
1722
void FilletBase::onDocumentRestored() {
1723
if(EdgeLinks.getSubValues().empty())
1725
Feature::onDocumentRestored();
1728
void FilletBase::syncEdgeLink() {
1729
if(!Base.getValue() || !Edges.getSize()) {
1730
EdgeLinks.setValue(0);
1733
std::vector<std::string> subs;
1734
std::string sub("Edge");
1735
for(auto &info : Edges.getValues())
1736
subs.emplace_back(sub+std::to_string(info.edgeid));
1737
EdgeLinks.setValue(Base.getValue(),subs);
1740
void FilletBase::onUpdateElementReference(const App::Property *prop) {
1741
if(prop!=&EdgeLinks || !getNameInDocument())
1743
auto values = Edges.getValues();
1744
const auto &subs = EdgeLinks.getSubValues();
1745
for(size_t i=0;i<values.size();++i) {
1746
if(i>=subs.size()) {
1747
FC_WARN("fillet edge count mismatch in object " << getFullName());
1751
sscanf(subs[i].c_str(),"Edge%d",&idx);
1753
values[i].edgeid = idx;
1755
FC_WARN("invalid fillet edge link '" << subs[i] << "' in object "
1758
Edges.setStatus(App::Property::User3,true);
1759
Edges.setValues(values);
1760
Edges.setStatus(App::Property::User3,false);
1765
PROPERTY_SOURCE(Part::FeatureExt, Part::Feature)
1771
PROPERTY_SOURCE_TEMPLATE(Part::FeaturePython, Part::Feature)
1772
template<> const char* Part::FeaturePython::getViewProviderName() const {
1773
return "PartGui::ViewProviderPython";
1775
template<> PyObject* Part::FeaturePython::getPyObject() {
1776
if (PythonObject.is(Py::_None())) {
1778
PythonObject = Py::Object(new FeaturePythonPyT<Part::PartFeaturePy>(this),true);
1780
return Py::new_reference_to(PythonObject);
1785
template class PartExport FeaturePythonT<Part::Feature>;
1789
std::vector<Part::cutFaces> Part::findAllFacesCutBy(
1790
const TopoDS_Shape& shape, const TopoDS_Shape& face, const gp_Dir& dir)
1794
BRepGProp::SurfaceProperties(face,props);
1795
gp_Pnt cog = props.CentreOfMass();
1798
gp_Lin line = gce_MakeLin(cog, dir);
1801
std::vector<cutFaces> result;
1802
BRepIntCurveSurface_Inter mkSection;
1805
for (mkSection.Init(shape, line, Precision::Confusion()); mkSection.More(); mkSection.Next()) {
1806
gp_Pnt iPnt = mkSection.Pnt();
1807
double dsq = cog.SquareDistance(iPnt);
1809
if (dsq < Precision::Confusion())
1813
gce_MakeDir mkDir(cog, iPnt);
1814
if (!mkDir.IsDone())
1817
if (mkDir.Value().IsOpposite(dir, Precision::Confusion()))
1821
newF.face = mkSection.Face();
1823
result.push_back(newF);
1829
std::vector<Part::cutTopoShapeFaces>
1830
Part::findAllFacesCutBy(const TopoShape& shape, const TopoShape& face, const gp_Dir& dir)
1834
BRepGProp::SurfaceProperties(face.getShape(), props);
1835
gp_Pnt cog = props.CentreOfMass();
1838
gp_Lin line = gce_MakeLin(cog, dir);
1841
std::vector<cutTopoShapeFaces> result;
1842
BRepIntCurveSurface_Inter mkSection;
1845
for (mkSection.Init(shape.getShape(), line, Precision::Confusion()); mkSection.More();
1847
gp_Pnt iPnt = mkSection.Pnt();
1848
double dsq = cog.SquareDistance(iPnt);
1850
if (dsq < Precision::Confusion()) {
1855
gce_MakeDir mkDir(cog, iPnt);
1856
if (!mkDir.IsDone()) {
1860
if (mkDir.Value().IsOpposite(dir, Precision::Confusion())) {
1864
cutTopoShapeFaces newF;
1865
newF.face = mkSection.Face();
1866
newF.face.mapSubElement(shape);
1868
result.push_back(newF);
1874
bool Part::checkIntersection(const TopoDS_Shape& first, const TopoDS_Shape& second,
1875
const bool quick, const bool touch_is_intersection) {
1877
Bnd_Box first_bb, second_bb;
1878
BRepBndLib::Add(first, first_bb);
1880
BRepBndLib::Add(second, second_bb);
1881
second_bb.SetGap(0);
1887
if (first_bb.IsOut(second_bb) && !touch_is_intersection)
1889
if (quick && !first_bb.IsOut(second_bb))
1892
if (touch_is_intersection) {
1894
BRepAlgoAPI_Fuse mkFuse(first, second);
1895
if (!mkFuse.IsDone())
1897
if (mkFuse.Shape().IsNull())
1902
xp.Init(mkFuse.Shape(),TopAbs_SOLID);
1906
return (xp.More() == Standard_False);
1912
BRepAlgoAPI_Common mkCommon(first, second);
1913
if (!mkCommon.IsDone())
1915
if (mkCommon.Shape().IsNull())
1920
xp.Init(mkCommon.Shape(),TopAbs_SOLID);
1921
return (xp.More() == Standard_True);