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,