TaskAttacher: Make sure hierarchy is respected when adding references.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
# include <BRepAdaptor_Surface.hxx>
|
||||
# include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
# include <BRepBuilderAPI_MakeFace.hxx>
|
||||
# include <BRepBuilderAPI_MakeVertex.hxx>
|
||||
# include <BRepExtrema_DistShapeShape.hxx>
|
||||
# include <BRepGProp.hxx>
|
||||
# include <BRepIntCurveSurface_Inter.hxx>
|
||||
@@ -820,7 +821,7 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector<const TopoD
|
||||
* subshapes are copied in the process (but copying a whole shape of an object can potentially be slow).
|
||||
*/
|
||||
void AttachEngine::readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
const std::vector<std::string> &sub,
|
||||
const std::vector<std::string> &subs,
|
||||
std::vector<App::GeoFeature*> &geofs,
|
||||
std::vector<const TopoDS_Shape*> &shapes,
|
||||
std::vector<TopoDS_Shape> &storage,
|
||||
@@ -831,50 +832,56 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
shapes.resize(objs.size());
|
||||
types.resize(objs.size());
|
||||
for (std::size_t i = 0; i < objs.size(); i++) {
|
||||
if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) {
|
||||
std::string fullSub = subs[i];
|
||||
const char* element = Data::findElementName(fullSub.c_str());
|
||||
App::DocumentObject* obj = objs[i]->getSubObject(subs[i].c_str());
|
||||
|
||||
auto* geof = dynamic_cast<App::GeoFeature*>(obj);
|
||||
if (!geof) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: attached to a non App::GeoFeature '"
|
||||
<< objs[i]->getNameInDocument() << "'");
|
||||
<< obj->getNameInDocument() << "'");
|
||||
}
|
||||
auto* geof = dynamic_cast<App::GeoFeature*>(objs[i]);
|
||||
geofs[i] = geof;
|
||||
Part::TopoShape shape;
|
||||
TopoDS_Shape myShape;
|
||||
Base::Placement plc = App::GeoFeature::getGlobalPlacement(obj, objs[i], fullSub);
|
||||
if (geof->isDerivedFrom(App::Plane::getClassTypeId())) {
|
||||
// obtain Z axis and origin of placement
|
||||
Base::Vector3d norm;
|
||||
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm);
|
||||
plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm);
|
||||
Base::Vector3d org;
|
||||
geof->Placement.getValue().multVec(Base::Vector3d(), org);
|
||||
plc.multVec(Base::Vector3d(), org);
|
||||
// make shape - an local-XY plane infinite face
|
||||
gp_Pln plane = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeFace(plane).Shape();
|
||||
myShape = BRepBuilderAPI_MakeFace(plane).Shape();
|
||||
myShape.Infinite(true);
|
||||
storage.emplace_back(myShape);
|
||||
shapes[i] = &(storage[storage.size() - 1]);
|
||||
}
|
||||
else if (geof->isDerivedFrom(App::Line::getClassTypeId())) {
|
||||
// obtain X axis and origin of placement
|
||||
// note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along
|
||||
// local Z.
|
||||
Base::Vector3d dir;
|
||||
geof->Placement.getValue().getRotation().multVec(Base::Vector3d(1.0, 0.0, 0.0), dir);
|
||||
plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), dir);
|
||||
Base::Vector3d org;
|
||||
geof->Placement.getValue().multVec(Base::Vector3d(), org);
|
||||
plc.multVec(Base::Vector3d(), org);
|
||||
// make shape - an infinite line along local X axis
|
||||
gp_Lin line = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z));
|
||||
TopoDS_Shape myShape = BRepBuilderAPI_MakeEdge(line).Shape();
|
||||
myShape = BRepBuilderAPI_MakeEdge(line).Shape();
|
||||
myShape.Infinite(true);
|
||||
storage.emplace_back(myShape);
|
||||
shapes[i] = &(storage[storage.size() - 1]);
|
||||
}
|
||||
else if (geof->isDerivedFrom(App::Point::getClassTypeId())) {
|
||||
Base::Vector3d org;
|
||||
plc.multVec(Base::Vector3d(), org);
|
||||
|
||||
gp_Pnt pnt = gp_Pnt(org.x, org.y, org.z);
|
||||
myShape = BRepBuilderAPI_MakeVertex(pnt).Shape();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true);
|
||||
Part::TopoShape shape = Part::Feature::getTopoShape(geof, element, true);
|
||||
for (;;) {
|
||||
if (shape.isNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found "
|
||||
<< objs[i]->getNameInDocument() << '.' << sub[i]);
|
||||
<< objs[i]->getNameInDocument() << '.' << subs[i]);
|
||||
}
|
||||
if (shape.shapeType() != TopAbs_COMPOUND
|
||||
|| shape.countSubShapes(TopAbs_SHAPE) != 1) {
|
||||
@@ -883,32 +890,34 @@ void AttachEngine::readLinks(const std::vector<App::DocumentObject*> &objs,
|
||||
// auto extract the single sub-shape from a compound
|
||||
shape = shape.getSubTopoShape(TopAbs_SHAPE, 1);
|
||||
}
|
||||
storage.emplace_back(shape.getShape());
|
||||
shape.setPlacement(plc);
|
||||
myShape = shape.getShape();
|
||||
}
|
||||
catch (Standard_Failure& e) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
|
||||
<< '.' << sub[i] << std::endl
|
||||
<< '.' << subs[i] << std::endl
|
||||
<< e.GetMessageString());
|
||||
}
|
||||
catch (Base::CADKernelError& e) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: subshape not found " << objs[i]->getNameInDocument()
|
||||
<< '.' << sub[i] << std::endl
|
||||
<< '.' << subs[i] << std::endl
|
||||
<< e.what());
|
||||
}
|
||||
if (storage.back().IsNull()) {
|
||||
if (myShape.IsNull()) {
|
||||
FC_THROWM(AttachEngineException,
|
||||
"AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.'
|
||||
<< sub[i]);
|
||||
<< subs[i]);
|
||||
}
|
||||
shapes[i] = &(storage.back());
|
||||
}
|
||||
storage.emplace_back(myShape);
|
||||
shapes[i] = &(storage.back());
|
||||
|
||||
// FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be
|
||||
// considered later, when the need arises.
|
||||
types[i] = getShapeType(*(shapes[i]));
|
||||
if (sub[i].length() == 0) {
|
||||
if (subs[i].length() == 0) {
|
||||
types[i] = eRefType(types[i] | rtFlagHasPlacement);
|
||||
}
|
||||
}
|
||||
@@ -977,6 +986,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement&
|
||||
for (auto obj : objs) {
|
||||
++i;
|
||||
auto& sub = subnames[i];
|
||||
obj = obj->getSubObject(sub.c_str());
|
||||
auto& shadow = shadowSubs[i];
|
||||
if (shadow.empty() || !Data::hasMissingElement(sub.c_str())) {
|
||||
continue;
|
||||
@@ -1176,9 +1186,13 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector<App::DocumentObjec
|
||||
}
|
||||
|
||||
// common stuff for all map modes
|
||||
gp_Pnt refOrg(0.0, 0.0, 0.0); // origin of linked object
|
||||
Base::Placement Place = parts[0]->Placement.getValue();
|
||||
refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
|
||||
Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]);
|
||||
Base::Console().Warning("parts[0] = %s\n", parts[0]->getNameInDocument());
|
||||
Base::Console().Warning("objs[0] = %s\n", objs[0]->getNameInDocument());
|
||||
Base::Console().Warning("subs[0] = %s\n", subs[0]);
|
||||
Base::Console().Warning("Place = (%f, %f, %f)\n", Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
|
||||
Base::Vector3d vec = Place.getPosition();
|
||||
gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object
|
||||
|
||||
// variables to derive the actual placement.
|
||||
// They are to be set, depending on the mode:
|
||||
@@ -2120,9 +2134,9 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector<App::DocumentObj
|
||||
|
||||
|
||||
// common stuff for all map modes
|
||||
gp_Pnt refOrg(0.0, 0.0, 0.0);
|
||||
Base::Placement Place = parts[0]->Placement.getValue();
|
||||
refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z);
|
||||
Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]);
|
||||
Base::Vector3d vec = Place.getPosition();
|
||||
gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object
|
||||
|
||||
// variables to derive the actual placement.
|
||||
// They are to be set, depending on the mode:
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <Gui/DocumentObserver.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Mod/Part/App/AttachExtension.h>
|
||||
#include <Mod/Part/App/DatumFeature.h>
|
||||
#include <Mod/Part/Gui/AttacherTexts.h>
|
||||
@@ -112,7 +113,7 @@ void TaskAttacher::makeRefStrings(std::vector<QString>& refstrings, std::vector<
|
||||
TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject* ViewProvider, QWidget* parent,
|
||||
QString picture, QString text, TaskAttacher::VisibilityFunction visFunc)
|
||||
: TaskBox(Gui::BitmapFactory().pixmap(picture.toLatin1()), text, true, parent)
|
||||
, SelectionObserver(ViewProvider)
|
||||
, SelectionObserver(ViewProvider, true, Gui::ResolveMode::NoResolve)
|
||||
, ViewProvider(ViewProvider)
|
||||
, ui(new Ui_TaskAttacher)
|
||||
, visibilityFunc(visFunc)
|
||||
@@ -365,6 +366,112 @@ QLineEdit* TaskAttacher::getLine(unsigned idx)
|
||||
}
|
||||
}
|
||||
|
||||
void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& sub)
|
||||
{
|
||||
// The reference that we store must take into account the hierarchy of geoFeatures. For example:
|
||||
// - Part
|
||||
// - - Cube
|
||||
// - Sketch
|
||||
// if sketch is attached to Cube.Face1 then it must store Part:Cube.Face3 as Sketch is outside of Part.
|
||||
// - Part
|
||||
// - - Cube
|
||||
// - - Sketch
|
||||
// In this example if must store Cube:Face3 because Sketch is inside Part, sibling of Cube.
|
||||
// So placement of Part is already taken into account.
|
||||
// - Part1
|
||||
// - - Part2
|
||||
// - - - Cube
|
||||
// - - Sketch
|
||||
// In this example it must store Part2:Cube.Face3 since Part1 is already taken into account.
|
||||
// - Part1
|
||||
// - - Part2
|
||||
// - - - Cube
|
||||
// - - Part3
|
||||
// - - - Sketch
|
||||
// In this example it's not possible because Sketch has Part3 placement. So it should be rejected
|
||||
// So we need to take the selection object and subname, and process them to get the correct obj/sub based
|
||||
// on attached and attaching objects positions.
|
||||
|
||||
std::vector<std::string> names = Base::Tools::splitSubName(sub);
|
||||
if (!rootObj || names.size() < 2) {
|
||||
return;
|
||||
}
|
||||
names.insert(names.begin(), rootObj->getNameInDocument());
|
||||
|
||||
App::Document* doc = rootObj->getDocument();
|
||||
App::DocumentObject* attachingObj = ViewProvider->getObject(); // Attaching object
|
||||
App::DocumentObject* subObj = rootObj->getSubObject(sub.c_str()); // Object being attached.
|
||||
if (!subObj || subObj == rootObj) {
|
||||
// Case of root object. We don't need to modify it.
|
||||
return;
|
||||
}
|
||||
if (subObj == attachingObj) {
|
||||
//prevent self-referencing
|
||||
rootObj = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if attachingObj is a root object. if so we keep the full path.
|
||||
auto* group = App::GeoFeatureGroupExtension::getGroupOfObject(attachingObj);
|
||||
if (!group) {
|
||||
if (attachingObj->getDocument() != rootObj->getDocument()) {
|
||||
// If it's not in same document then it's not a good selection
|
||||
rootObj = nullptr;
|
||||
}
|
||||
// if it's same document we keep the rootObj and sub unchanged.
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
App::DocumentObject* obj = doc->getObject(names[i].c_str());
|
||||
if (!obj) {
|
||||
Base::Console().TranslatedUserError("TaskAttacher",
|
||||
"Unsuitable selection: '%s' cannot be attached to '%s' from within it's group '%s'.\n",
|
||||
attachingObj->getFullLabel(), subObj->getFullLabel(), group->getFullLabel());
|
||||
rootObj = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// In case the attaching object is in a link to a part.
|
||||
// For instance :
|
||||
// - Part1
|
||||
// - - LinkToPart2
|
||||
// - - - Cube
|
||||
// - - - Sketch
|
||||
obj = obj->getLinkedObject();
|
||||
|
||||
if (obj == group) {
|
||||
++i;
|
||||
obj = doc->getObject(names[i].c_str());
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootObj = obj;
|
||||
|
||||
// Rebuild 'sub' starting from the next element after the current 'name'
|
||||
sub = "";
|
||||
for (size_t j = i + 1; j < names.size(); ++j) {
|
||||
sub += names[j];
|
||||
if (j != names.size() - 1) {
|
||||
sub += "."; // Add a period between elements
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach this point it means that attaching object's group is outside of
|
||||
// the scope of the attached object. For instance:
|
||||
// - Part1
|
||||
// - - Part2
|
||||
// - - - Cube
|
||||
// - - Part3
|
||||
// - - - Sketch
|
||||
// In this case the selection is not acceptable.
|
||||
rootObj = nullptr;
|
||||
}
|
||||
|
||||
void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
{
|
||||
if (!ViewProvider) {
|
||||
@@ -377,14 +484,17 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
}
|
||||
|
||||
// Note: The validity checking has already been done in ReferenceSelection.cpp
|
||||
Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType<Part::AttachExtension>();
|
||||
App::DocumentObject* obj = ViewProvider->getObject();
|
||||
Part::AttachExtension* pcAttach = obj->getExtensionByType<Part::AttachExtension>();
|
||||
std::vector<App::DocumentObject*> refs = pcAttach->AttachmentSupport.getValues();
|
||||
std::vector<std::string> refnames = pcAttach->AttachmentSupport.getSubValues();
|
||||
App::DocumentObject* selObj = ViewProvider->getObject()->getDocument()->getObject(msg.pObjectName);
|
||||
if (!selObj || selObj == ViewProvider->getObject())//prevent self-referencing
|
||||
return;
|
||||
|
||||
App::DocumentObject* selObj = obj->getDocument()->getObject(msg.pObjectName);
|
||||
std::string subname = msg.pSubName;
|
||||
processSelection(selObj, subname);
|
||||
if (!selObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove subname for planes and datum features
|
||||
if (selObj->isDerivedFrom<App::DatumElement>() || selObj->isDerivedFrom<Part::Datum>()) {
|
||||
|
||||
@@ -113,6 +113,8 @@ private:
|
||||
void updateRefButton(int idx);
|
||||
void updateAttachmentOffsetUI();
|
||||
|
||||
void processSelection(App::DocumentObject*& obj, std::string& sub);
|
||||
|
||||
/**
|
||||
* @brief updateListOfModes Fills the mode list with modes that apply to
|
||||
* current set of references. Maintains selection when possible.
|
||||
|
||||
Reference in New Issue
Block a user