Files
create/src/Mod/Mesh/Gui/MeshSelection.cpp
wmayer 184afd9761 Gui: fix View3DInventorViewer's getViewProviderByPath, getViewProviderByPathFromTail, getViewProvidersOfType
The View3DInventorViewer only contains the top-level view providers which caused unexpected behaviour when using the three methods above. Thus, in client code they haven't been used any more.
Now the methods internally invoke the corresponding methods of the document. This allows it in client code to directly call the appropriate methods again (LoD).
2022-12-12 16:41:58 +01:00

579 lines
21 KiB
C++

/***************************************************************************
* Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <climits>
# include <QBitmap>
# include <Inventor/SbBox2s.h>
# include <Inventor/SoPickedPoint.h>
# include <Inventor/details/SoFaceDetail.h>
# include <Inventor/events/SoMouseButtonEvent.h>
# include <Inventor/nodes/SoCamera.h>
#endif
#include <App/Application.h>
#include <App/Document.h>
#include <Base/Console.h>
#include <Base/Tools.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/MouseSelection.h>
#include <Gui/NavigationStyle.h>
#include <Gui/Utilities.h>
#include <Gui/View3DInventor.h>
#include <Gui/View3DInventorViewer.h>
#include <Mod/Mesh/App/MeshFeature.h>
#include <Mod/Mesh/App/Core/MeshKernel.h>
#include <Mod/Mesh/App/Core/Iterator.h>
#include <Mod/Mesh/App/Core/TopoAlgorithm.h>
#include "MeshSelection.h"
#include "ViewProvider.h"
using namespace MeshGui;
#define CROSS_WIDTH 16
#define CROSS_HEIGHT 16
#define CROSS_HOT_X 7
#define CROSS_HOT_Y 7
unsigned char MeshSelection::cross_bitmap[] = {
0xc0, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
0x40, 0x02, 0x40, 0x02, 0x7f, 0xfe, 0x01, 0x80,
0x01, 0x80, 0x7f, 0xfe, 0x40, 0x02, 0x40, 0x02,
0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0xc0, 0x03
};
unsigned char MeshSelection::cross_mask_bitmap[] = {
0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03,
0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03,
0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03
};
MeshSelection::MeshSelection()
: onlyPointToUserTriangles(false)
, onlyVisibleTriangles(false)
, addToSelection(false)
, addComponent(false)
, removeComponent(false)
, activeCB(nullptr)
, selectionCB(nullptr)
, ivViewer(nullptr)
{
setCallback(selectGLCallback);
}
MeshSelection::~MeshSelection()
{
if (this->activeCB) {
Gui::View3DInventorViewer* viewer = this->getViewer();
if (viewer)
stopInteractiveCallback(viewer);
}
}
void MeshSelection::setEnabledViewerSelection(bool on)
{
Gui::View3DInventorViewer* viewer = this->getViewer();
if (viewer) {
viewer->setSelectionEnabled(on);
}
}
void MeshSelection::setCallback(SoEventCallbackCB *cb)
{
selectionCB = cb;
}
void MeshSelection::setObjects(const std::vector<Gui::SelectionObject>& obj)
{
meshObjects = obj;
}
std::vector<App::DocumentObject*> MeshSelection::getObjects() const
{
std::vector<App::DocumentObject*> objs;
if (!meshObjects.empty()) {
for (std::vector<Gui::SelectionObject>::iterator it = meshObjects.begin(); it != meshObjects.end(); ++it) {
App::DocumentObject* obj = it->getObject();
if (obj) {
objs.push_back(obj);
}
}
}
// get all objects of the active document
else {
App::Document* doc = App::GetApplication().getActiveDocument();
if (doc)
objs = doc->getObjectsOfType(Mesh::Feature::getClassTypeId());
}
return objs;
}
std::list<ViewProviderMesh*> MeshSelection::getViewProviders() const
{
std::vector<App::DocumentObject*> objs = getObjects();
std::list<ViewProviderMesh*> vps;
for (std::vector<App::DocumentObject*>::iterator it = objs.begin(); it != objs.end(); ++it) {
if ((*it)->isDerivedFrom(Mesh::Feature::getClassTypeId())) {
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(*it);
if (vp->isVisible())
vps.push_back(static_cast<ViewProviderMesh*>(vp));
}
}
return vps;
}
void MeshSelection::setViewer(Gui::View3DInventorViewer* v)
{
ivViewer = v;
}
Gui::View3DInventorViewer* MeshSelection::getViewer() const
{
// if a special viewer was set from outside then use this
if (ivViewer)
return ivViewer;
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (!doc)
return nullptr;
Gui::MDIView* view = doc->getActiveView();
if (view && view->getTypeId().isDerivedFrom(Gui::View3DInventor::getClassTypeId())) {
Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
return viewer;
}
return nullptr;
}
void MeshSelection::startInteractiveCallback(Gui::View3DInventorViewer* viewer,SoEventCallbackCB *cb)
{
if (this->activeCB)
return;
viewer->setEditing(true);
viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), cb, this);
this->activeCB = cb;
}
void MeshSelection::stopInteractiveCallback(Gui::View3DInventorViewer* viewer)
{
if (!this->activeCB)
return;
viewer->setEditing(false);
viewer->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), this->activeCB, this);
this->activeCB = nullptr;
}
void MeshSelection::prepareFreehandSelection(bool add,SoEventCallbackCB *cb)
{
// a rubberband to select a rectangle area of the meshes
Gui::View3DInventorViewer* viewer = this->getViewer();
if (viewer) {
// Note: It is possible that the mouse selection mode can be stopped
// but then the callback function is still active.
stopInteractiveCallback(viewer);
startInteractiveCallback(viewer, cb);
viewer->navigationStyle()->stopSelection();
// set cross cursor
Gui::FreehandSelection* freehand = new Gui::FreehandSelection();
freehand->setClosed(true);
freehand->setColor(1.0f, 0.0f, 0.0f);
freehand->setLineWidth(3.0f);
viewer->navigationStyle()->startSelection(freehand);
auto setComponentCursor = [=]() {
QBitmap cursor = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_bitmap);
QBitmap mask = QBitmap::fromData(QSize(CROSS_WIDTH, CROSS_HEIGHT), cross_mask_bitmap);
#if defined(Q_OS_WIN32)
double dpr = viewer->devicePixelRatio();
cursor.setDevicePixelRatio(dpr);
mask.setDevicePixelRatio(dpr);
#endif
QCursor custom(cursor, mask, CROSS_HOT_X, CROSS_HOT_Y);
viewer->setComponentCursor(custom);
};
QObject::connect(viewer, &Gui::View3DInventorViewer::devicePixelRatioChanged, setComponentCursor);
setComponentCursor();
this->addToSelection = add;
}
}
void MeshSelection::startSelection()
{
prepareFreehandSelection(true, selectionCB);
}
void MeshSelection::startDeselection()
{
prepareFreehandSelection(false, selectionCB);
}
void MeshSelection::stopSelection()
{
Gui::View3DInventorViewer* viewer = getViewer();
if (viewer) {
stopInteractiveCallback(viewer);
viewer->navigationStyle()->stopSelection();
}
}
void MeshSelection::fullSelection()
{
// select the complete meshes
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
Mesh::Feature* mf = static_cast<Mesh::Feature*>((*it)->getObject());
const Mesh::MeshObject* mo = mf->Mesh.getValuePtr();
std::vector<Mesh::FacetIndex> faces(mo->countFacets());
std::generate(faces.begin(), faces.end(), Base::iotaGen<Mesh::FacetIndex>(0));
(*it)->addSelection(faces);
}
}
void MeshSelection::clearSelection()
{
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
(*it)->clearSelection();
}
}
bool MeshSelection::deleteSelection()
{
// delete all selected faces
bool selected = false;
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
Mesh::Feature* mf = static_cast<Mesh::Feature*>((*it)->getObject());
unsigned long ct = MeshCore::MeshAlgorithm(mf->Mesh.getValue().getKernel()).
CountFacetFlag(MeshCore::MeshFacet::SELECTED);
if (ct > 0) {
selected = true;
break;
}
}
if (!selected)
return false; // nothing todo
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
(*it)->deleteSelection();
}
return true;
}
bool MeshSelection::deleteSelectionBorder()
{
// delete all selected faces
bool deletion = false;
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
Mesh::Feature* mf = static_cast<Mesh::Feature*>((*it)->getObject());
// mark the selected facet as visited
std::vector<Mesh::FacetIndex> selection, remove;
std::set<Mesh::PointIndex> borderPoints;
MeshCore::MeshAlgorithm meshAlg(mf->Mesh.getValue().getKernel());
meshAlg.GetFacetsFlag(selection, MeshCore::MeshFacet::SELECTED);
meshAlg.GetBorderPoints(selection, borderPoints);
std::vector<Mesh::PointIndex> border;
border.insert(border.begin(), borderPoints.begin(), borderPoints.end());
meshAlg.ResetFacetFlag(MeshCore::MeshFacet::VISIT);
meshAlg.SetFacetsFlag(selection, MeshCore::MeshFacet::VISIT);
meshAlg.ResetPointFlag(MeshCore::MeshPoint::VISIT);
meshAlg.SetPointsFlag(border, MeshCore::MeshPoint::VISIT);
// collect neighbour facets that are not selected and that share a border point
const MeshCore::MeshPointArray& points = mf->Mesh.getValue().getKernel().GetPoints();
const MeshCore::MeshFacetArray& faces = mf->Mesh.getValue().getKernel().GetFacets();
unsigned long numFaces = faces.size();
for (unsigned long i = 0; i < numFaces; i++) {
const MeshCore::MeshFacet& face = faces[i];
if (!face.IsFlag(MeshCore::MeshFacet::VISIT)) {
for (int j=0; j<3; j++) {
if (points[face._aulPoints[j]].IsFlag(MeshCore::MeshPoint::VISIT)) {
remove.push_back(i);
break;
}
}
}
}
if (!remove.empty()) {
deletion = true;
// remove duplicates
std::sort(remove.begin(), remove.end());
remove.erase(std::unique(remove.begin(), remove.end()), remove.end());
(*it)->setSelection(remove);
(*it)->deleteSelection();
}
}
return deletion;
}
void MeshSelection::invertSelection()
{
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
(*it)->invertSelection();
}
}
void MeshSelection::selectComponent(int size)
{
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
Mesh::Feature* mf = static_cast<Mesh::Feature*>((*it)->getObject());
const Mesh::MeshObject* mo = mf->Mesh.getValuePtr();
std::vector<std::vector<Mesh::FacetIndex> > segm;
MeshCore::MeshComponents comp(mo->getKernel());
comp.SearchForComponents(MeshCore::MeshComponents::OverEdge,segm);
std::vector<Mesh::FacetIndex> faces;
for (std::vector<std::vector<Mesh::FacetIndex> >::iterator jt = segm.begin(); jt != segm.end(); ++jt) {
if (jt->size() < (Mesh::FacetIndex)size)
faces.insert(faces.end(), jt->begin(), jt->end());
}
(*it)->addSelection(faces);
}
}
void MeshSelection::deselectComponent(int size)
{
std::list<ViewProviderMesh*> views = getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
Mesh::Feature* mf = static_cast<Mesh::Feature*>((*it)->getObject());
const Mesh::MeshObject* mo = mf->Mesh.getValuePtr();
std::vector<std::vector<Mesh::FacetIndex> > segm;
MeshCore::MeshComponents comp(mo->getKernel());
comp.SearchForComponents(MeshCore::MeshComponents::OverEdge,segm);
std::vector<Mesh::FacetIndex> faces;
for (std::vector<std::vector<Mesh::FacetIndex> >::iterator jt = segm.begin(); jt != segm.end(); ++jt) {
if (jt->size() > (Mesh::FacetIndex)size)
faces.insert(faces.end(), jt->begin(), jt->end());
}
(*it)->removeSelection(faces);
}
}
void MeshSelection::selectTriangle()
{
this->addToSelection = true;
Gui::View3DInventorViewer* viewer = this->getViewer();
if (viewer) {
stopInteractiveCallback(viewer);
viewer->navigationStyle()->stopSelection();
startInteractiveCallback(viewer, pickFaceCallback);
viewer->setEditingCursor(QCursor(Qt::PointingHandCursor));
}
}
void MeshSelection::deselectTriangle()
{
this->addToSelection = false;
Gui::View3DInventorViewer* viewer = this->getViewer();
if (viewer) {
stopInteractiveCallback(viewer);
viewer->navigationStyle()->stopSelection();
startInteractiveCallback(viewer, pickFaceCallback);
viewer->setEditingCursor(QCursor(Qt::PointingHandCursor));
}
}
void MeshSelection::setCheckOnlyPointToUserTriangles(bool on)
{
onlyPointToUserTriangles = on;
}
bool MeshSelection::isCheckedOnlyPointToUserTriangles() const
{
return onlyPointToUserTriangles;
}
void MeshSelection::setCheckOnlyVisibleTriangles(bool on)
{
onlyVisibleTriangles = on;
}
bool MeshSelection::isCheckedOnlyVisibleTriangles() const
{
return onlyVisibleTriangles;
}
void MeshSelection::setAddComponentOnClick(bool on)
{
addComponent = on;
}
void MeshSelection::setRemoveComponentOnClick(bool on)
{
removeComponent = on;
}
void MeshSelection::selectGLCallback(void * ud, SoEventCallback * n)
{
// When this callback function is invoked we must leave the edit mode
Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
MeshSelection* self = static_cast<MeshSelection*>(ud);
self->stopInteractiveCallback(view);
n->setHandled();
std::vector<SbVec2f> polygon = view->getGLPolygon();
if (polygon.size() < 3)
return;
if (polygon.front() != polygon.back())
polygon.push_back(polygon.front());
SbVec3f pnt, dir;
view->getNearPlane(pnt, dir);
Base::Vector3f point (pnt[0],pnt[1],pnt[2]);
Base::Vector3f normal(dir[0],dir[1],dir[2]);
std::list<ViewProviderMesh*> views = self->getViewProviders();
for (std::list<ViewProviderMesh*>::iterator it = views.begin(); it != views.end(); ++it) {
ViewProviderMesh* vp = *it;
std::vector<Mesh::FacetIndex> faces;
const Mesh::MeshObject& mesh = static_cast<Mesh::Feature*>((*it)->getObject())->Mesh.getValue();
const MeshCore::MeshKernel& kernel = mesh.getKernel();
// simply get all triangles under the polygon
SoCamera* cam = view->getSoRenderManager()->getCamera();
SbViewVolume vv = cam->getViewVolume();
Gui::ViewVolumeProjection proj(vv);
Base::Placement plm = static_cast<Mesh::Feature*>(vp->getObject())->Placement.getValue();
proj.setTransform(plm.toMatrix());
vp->getFacetsFromPolygon(polygon, proj, true, faces);
if (self->onlyVisibleTriangles) {
const SbVec2s& sz = view->getSoRenderManager()->getViewportRegion().getWindowSize();
short width,height; sz.getValue(width,height);
std::vector<SbVec2s> pixelPoly = view->getPolygon();
SbBox2s rect;
for (std::vector<SbVec2s>::iterator it = pixelPoly.begin(); it != pixelPoly.end(); ++it) {
const SbVec2s& p = *it;
rect.extendBy(SbVec2s(p[0],height-p[1]));
}
std::vector<Mesh::FacetIndex> rf; rf.swap(faces);
std::vector<Mesh::FacetIndex> vf = vp->getVisibleFacetsAfterZoom
(rect, view->getSoRenderManager()->getViewportRegion(), view->getSoRenderManager()->getCamera());
// get common facets of the viewport and the visible one
std::sort(vf.begin(), vf.end());
std::sort(rf.begin(), rf.end());
std::back_insert_iterator<std::vector<Mesh::FacetIndex> > biit(faces);
std::set_intersection(vf.begin(), vf.end(), rf.begin(), rf.end(), biit);
}
// if set filter out all triangles which do not point into user direction
if (self->onlyPointToUserTriangles) {
std::vector<Mesh::FacetIndex> screen;
screen.reserve(faces.size());
MeshCore::MeshFacetIterator it_f(kernel);
for (std::vector<Mesh::FacetIndex>::iterator it = faces.begin(); it != faces.end(); ++it) {
it_f.Set(*it);
if (it_f->GetNormal() * normal > 0.0f) {
screen.push_back(*it);
}
}
faces.swap(screen);
}
if (self->addToSelection)
vp->addSelection(faces);
else
vp->removeSelection(faces);
}
view->redraw();
}
void MeshSelection::pickFaceCallback(void * ud, SoEventCallback * n)
{
// handle only mouse button events
if (n->getEvent()->isOfType(SoMouseButtonEvent::getClassTypeId())) {
const SoMouseButtonEvent * mbe = static_cast<const SoMouseButtonEvent*>(n->getEvent());
Gui::View3DInventorViewer* view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
// Mark all incoming mouse button events as handled, especially, to deactivate the selection node
n->getAction()->setHandled();
if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) {
const SoPickedPoint * point = n->getPickedPoint();
if (!point) {
Base::Console().Message("No facet picked.\n");
return;
}
n->setHandled();
// By specifying the indexed mesh node 'pcFaceSet' we make sure that the picked point is
// really from the mesh we render and not from any other geometry
Gui::ViewProvider* vp = view->getViewProviderByPathFromTail(point->getPath());
if (!vp || !vp->getTypeId().isDerivedFrom(ViewProviderMesh::getClassTypeId()))
return;
ViewProviderMesh* mesh = static_cast<ViewProviderMesh*>(vp);
MeshSelection* self = static_cast<MeshSelection*>(ud);
std::list<ViewProviderMesh*> views = self->getViewProviders();
if (std::find(views.begin(), views.end(), mesh) == views.end())
return;
const SoDetail* detail = point->getDetail(/*mesh->getShapeNode()*/);
if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) {
// get the boundary to the picked facet
Mesh::FacetIndex uFacet = static_cast<const SoFaceDetail*>(detail)->getFaceIndex();
if (self->addToSelection) {
if (self->addComponent)
mesh->selectComponent(uFacet);
else
mesh->selectFacet(uFacet);
}
else {
if (self->removeComponent)
mesh->deselectComponent(uFacet);
else
mesh->deselectFacet(uFacet);
}
}
}
}
}