diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp
index dc4ee87fe2..57179a3b5d 100644
--- a/src/Gui/CommandView.cpp
+++ b/src/Gui/CommandView.cpp
@@ -70,6 +70,7 @@
#include "NavigationStyle.h"
#include
+#include
#include
#include
#include
@@ -80,6 +81,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -785,6 +788,7 @@ StdCmdToggleVisibility::StdCmdToggleVisibility()
eType = Alter3DView;
}
+
void StdCmdToggleVisibility::activated(int iMsg)
{
Q_UNUSED(iMsg);
@@ -2463,14 +2467,139 @@ StdBoxSelection::StdBoxSelection()
eType = AlterSelection;
}
+typedef enum { CENTER, INTERSECT } SelectionMode;
+
+static std::vector getBoxSelection(
+ ViewProviderDocumentObject *vp, SelectionMode mode, bool selectElement,
+ const Base::ViewProjMethod &proj, const Base::Polygon2d &polygon,
+ const Base::Matrix4D &mat, bool transform=true, int depth=0)
+{
+ std::vector ret;
+ auto obj = vp->getObject();
+ if(!obj || !obj->getNameInDocument())
+ return ret;
+
+ // DO NOT check this view object Visibility, let the caller do this. Because
+ // we may be called by upper object hierarchy that manages our visibility.
+
+ auto bbox3 = vp->getBoundingBox(0,transform);
+ if(!bbox3.IsValid())
+ return ret;
+
+ auto bbox = bbox3.Transformed(mat).ProjectBox(&proj);
+
+ // check if both two boundary points are inside polygon, only
+ // valid since we know the given polygon is a box.
+ if(polygon.Contains(Base::Vector2d(bbox.MinX,bbox.MinY)) &&
+ polygon.Contains(Base::Vector2d(bbox.MaxX,bbox.MaxY)))
+ {
+ ret.emplace_back("");
+ return ret;
+ }
+
+ if(!bbox.Intersect(polygon))
+ return ret;
+
+ const auto &subs = obj->getSubObjects(App::DocumentObject::GS_SELECT);
+ if(subs.empty()) {
+ if(!selectElement) {
+ if(mode==INTERSECT || bbox.Contains(bbox.GetCenter()))
+ ret.emplace_back("");
+ return ret;
+ }
+ Base::PyGILStateLocker lock;
+ PyObject *pyobj = 0;
+ Base::Matrix4D matCopy(mat);
+ obj->getSubObject(0,&pyobj,&matCopy,transform,depth);
+ if(!pyobj)
+ return ret;
+ Py::Object pyobject(pyobj,true);
+ if(!PyObject_TypeCheck(pyobj,&Data::ComplexGeoDataPy::Type))
+ return ret;
+ auto data = static_cast(pyobj)->getComplexGeoDataPtr();
+ for(auto type : data->getElementTypes()) {
+ size_t count = data->countSubElements(type);
+ if(!count)
+ continue;
+ for(size_t i=1;i<=count;++i) {
+ std::string element(type);
+ element += std::to_string(i);
+ std::unique_ptr segment(data->getSubElementByName(element.c_str()));
+ if(!segment)
+ continue;
+ std::vector points;
+ std::vector lines;
+ data->getLinesFromSubelement(segment.get(),points,lines);
+ if(lines.empty()) {
+ if(points.empty())
+ continue;
+ auto v = proj(points[0]);
+ if(polygon.Contains(Base::Vector2d(v.x,v.y)))
+ ret.push_back(element);
+ continue;
+ }
+ Base::Polygon2d loop;
+ // TODO: can we assume the line returned above are in proper
+ // order if the element is a face?
+ auto v = proj(points[lines.front().I1]);
+ loop.Add(Base::Vector2d(v.x,v.y));
+ for(auto &line : lines) {
+ for(auto i=line.I1;iresolve(sub.c_str(),&parent,&childName,0,0,&smat,transform,depth+1);
+ if(!sobj)
+ continue;
+ int vis;
+ if(!parent || (vis=parent->isElementVisible(childName.c_str()))<0)
+ vis = sobj->Visibility.getValue()?1:0;
+
+ if(!vis)
+ continue;
+
+ auto svp = dynamic_cast(Application::Instance->getViewProvider(sobj));
+ if(!svp)
+ continue;
+
+ const auto &sels = getBoxSelection(svp,mode,selectElement,proj,polygon,smat,false,depth+1);
+ if(sels.size()==1 && sels[0] == "")
+ ++count;
+ for(auto &sel : sels)
+ ret.emplace_back(sub+sel);
+ }
+ if(count==subs.size()) {
+ ret.resize(1);
+ ret[0].clear();
+ }
+ return ret;
+}
+
static void selectionCallback(void * ud, SoEventCallback * cb)
{
+ bool selectElement = ud?true:false;
Gui::View3DInventorViewer* view = reinterpret_cast(cb->getUserData());
view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback, ud);
SoNode* root = view->getSceneGraph();
static_cast(root)->selectionRole.setValue(true);
- typedef enum { CENTER, INTERSECT } SelectionMode;
SelectionMode selectionMode = CENTER;
std::vector picked = view->getGLPolygon();
@@ -2505,33 +2634,17 @@ static void selectionCallback(void * ud, SoEventCallback * cb)
Gui::Selection().clearSelection(doc->getName());
}
- std::vector geom = doc->getObjectsOfType();
- for (std::vector::iterator it = geom.begin(); it != geom.end(); ++it) {
- Gui::ViewProvider* vp = Application::Instance->getViewProvider(*it);
- if (!vp->isVisible())
+ for(auto obj : doc->getObjects()) {
+ if(App::GeoFeatureGroupExtension::getGroupOfObject(obj))
continue;
- // 0002706: For box selection use SoGetBoundingBoxAction for
- // the view provider of an object
- SoGetBoundingBoxAction bboxAction(view->getViewportRegion());
- bboxAction.apply(vp->getRoot());
- SbBox3f bbox = bboxAction.getBoundingBox();
- Base::BoundBox3d bbox3;
- bbox3.Add(Base::convertTo(bbox.getMax()));
- bbox3.Add(Base::convertTo(bbox.getMin()));
- if (selectionMode == CENTER) {
- Base::Vector3d pt2d;
- pt2d = proj(bbox3.GetCenter());
- if (polygon.Contains(Base::Vector2d(pt2d.x, pt2d.y))) {
- Gui::Selection().addSelection(doc->getName(), (*it)->getNameInDocument());
- }
- }
- else {
- Base::BoundBox2d bbox2 = bbox3.ProjectBox(&proj);
- if (bbox2.Intersect(polygon)) {
- Gui::Selection().addSelection(doc->getName(), (*it)->getNameInDocument());
- }
- }
+ auto vp = dynamic_cast(Application::Instance->getViewProvider(obj));
+ if (!vp || !vp->isVisible())
+ continue;
+
+ Base::Matrix4D mat;
+ for(auto &sub : getBoxSelection(vp,selectionMode,selectElement,proj,polygon,mat))
+ Gui::Selection().addSelection(doc->getName(), obj->getNameInDocument(), sub.c_str());
}
}
}
@@ -2558,6 +2671,49 @@ void StdBoxSelection::activated(int iMsg)
}
}
+//===========================================================================
+// Std_BoxElementSelection
+//===========================================================================
+DEF_3DV_CMD(StdBoxElementSelection)
+
+StdBoxElementSelection::StdBoxElementSelection()
+ : Command("Std_BoxElementSelection")
+{
+ sGroup = QT_TR_NOOP("Standard-View");
+ sMenuText = QT_TR_NOOP("Box element selection");
+ sToolTipText = QT_TR_NOOP("Box element selection");
+ sWhatsThis = "Std_BoxElementSelection";
+ sStatusTip = QT_TR_NOOP("Box element selection");
+#if QT_VERSION >= 0x040200
+ sPixmap = "edit-element-select-box";
+#endif
+ sAccel = "Shift+E";
+ eType = AlterSelection;
+}
+
+void StdBoxElementSelection::activated(int iMsg)
+{
+ Q_UNUSED(iMsg);
+ View3DInventor* view = qobject_cast(getMainWindow()->activeWindow());
+ if (view) {
+ View3DInventorViewer* viewer = view->getViewer();
+ if (!viewer->isSelecting()) {
+ // #0002931: Box select misbehaves with touchpad navigation style
+ // Notify the navigation style to cleanup internal states
+ int mode = viewer->navigationStyle()->getViewingMode();
+ if (mode != Gui::NavigationStyle::IDLE) {
+ SoKeyboardEvent ev;
+ viewer->navigationStyle()->processEvent(&ev);
+ }
+ viewer->startSelection(View3DInventorViewer::Rubberband);
+ viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback, this);
+ SoNode* root = viewer->getSceneGraph();
+ static_cast(root)->selectionRole.setValue(false);
+ }
+ }
+}
+
+
//===========================================================================
// Std_TreeSelection
//===========================================================================
@@ -3350,7 +3506,7 @@ void CreateViewStdCommands(void)
rcCmdMgr.addCommand(new StdViewZoomOut());
rcCmdMgr.addCommand(new StdViewBoxZoom());
rcCmdMgr.addCommand(new StdBoxSelection());
- rcCmdMgr.addCommand(new StdCmdTreeSelection());
+ rcCmdMgr.addCommand(new StdBoxElementSelection());
rcCmdMgr.addCommand(new StdCmdTreeExpand());
rcCmdMgr.addCommand(new StdCmdTreeCollapse());
rcCmdMgr.addCommand(new StdCmdTreeSelectAllInstances());
diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp
index 324ba63551..d68d01fd3b 100644
--- a/src/Gui/Workbench.cpp
+++ b/src/Gui/Workbench.cpp
@@ -534,7 +534,7 @@ MenuItem* StdWorkbench::setupMenuBar() const
edit->setCommand("&Edit");
*edit << "Std_Undo" << "Std_Redo" << "Separator" << "Std_Cut" << "Std_Copy"
<< "Std_Paste" << "Std_DuplicateSelection" << "Separator"
- << "Std_Refresh" << "Std_BoxSelection" << "Std_SelectAll" << "Std_Delete"
+ << "Std_Refresh" << "Std_BoxSelection" << "Std_BoxElementSelection" << "Std_SelectAll" << "Std_Delete"
<< "Separator" << "Std_Placement" /*<< "Std_TransformManip"*/ << "Std_Alignment"
<< "Std_Edit" << "Separator" << "Std_DlgPreferences";
diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp
index 54a17137d6..91ef2d01c3 100644
--- a/src/Mod/Part/App/TopoShape.cpp
+++ b/src/Mod/Part/App/TopoShape.cpp
@@ -3415,12 +3415,90 @@ void TopoShape::getPoints(std::vector &Points,
}
void TopoShape::getLinesFromSubelement(const Data::Segment* element,
- std::vector &Points,
+ std::vector &vertices,
std::vector &lines) const
{
- (void)element;
- (void)Points;
- (void)lines;
+ if (element->getTypeId() == ShapeSegment::getClassTypeId()) {
+ const TopoDS_Shape& shape = static_cast(element)->Shape;
+ if (shape.IsNull())
+ return;
+ if(shape.ShapeType() == TopAbs_VERTEX) {
+ auto pnt = BRep_Tool::Pnt(TopoDS::Vertex(shape));
+ vertices.emplace_back(pnt.X(),pnt.Y(),pnt.Z());
+ return;
+ }
+
+ for(TopExp_Explorer exp(shape,TopAbs_EDGE);exp.More();exp.Next()) {
+
+ TopoDS_Edge aEdge = TopoDS::Edge(exp.Current());
+ TopLoc_Location aLoc;
+ Handle(Poly_Polygon3D) aPoly = BRep_Tool::Polygon3D(aEdge, aLoc);
+
+ gp_Trsf myTransf;
+ Standard_Integer nbNodesInFace;
+
+ auto line_start = vertices.size();
+
+ // triangulation succeeded?
+ if (!aPoly.IsNull()) {
+ if (!aLoc.IsIdentity()) {
+ myTransf = aLoc.Transformation();
+ }
+ nbNodesInFace = aPoly->NbNodes();
+
+ const TColgp_Array1OfPnt& Nodes = aPoly->Nodes();
+
+ gp_Pnt V;
+ for (Standard_Integer i=0;i < nbNodesInFace;i++) {
+ V = Nodes(i+1);
+ V.Transform(myTransf);
+ vertices.emplace_back(V.X(),V.Y(),V.Z());
+ }
+ }
+ else {
+ // the edge has not its own triangulation, but then a face the edge is attached to
+ // must provide this triangulation
+
+ // Look for one face in our map (it doesn't care which one we take)
+ auto aFace = findAncestorShape(aEdge, TopAbs_FACE);
+ if(aFace.IsNull())
+ continue;
+
+ // take the face's triangulation instead
+ Handle(Poly_Triangulation) aPolyTria = BRep_Tool::Triangulation(TopoDS::Face(aFace),aLoc);
+ if (!aLoc.IsIdentity()) {
+ myTransf = aLoc.Transformation();
+ }
+
+ if (aPolyTria.IsNull()) break;
+
+ // this holds the indices of the edge's triangulation to the actual points
+ Handle(Poly_PolygonOnTriangulation) aPoly = BRep_Tool::PolygonOnTriangulation(aEdge, aPolyTria, aLoc);
+ if (aPoly.IsNull())
+ continue; // polygon does not exist
+
+ // getting size and create the array
+ nbNodesInFace = aPoly->NbNodes();
+
+ const TColStd_Array1OfInteger& indices = aPoly->Nodes();
+ const TColgp_Array1OfPnt& Nodes = aPolyTria->Nodes();
+
+ gp_Pnt V;
+ // go through the index array
+ for (Standard_Integer i=indices.Lower();i <= indices.Upper();i++) {
+ V = Nodes(indices(i));
+ V.Transform(myTransf);
+ vertices.emplace_back(V.X(),V.Y(),V.Z());
+ }
+ }
+
+ if(line_start+1 < vertices.size()) {
+ lines.emplace_back();
+ lines.back().I1 = line_start;
+ lines.back().I2 = vertices.size()-1;
+ }
+ }
+ }
}
void TopoShape::getFacesFromSubelement(const Data::Segment* element,