Sketcher: implement box selection (based on mrlukeparry's work)

This commit is contained in:
logari81 2012-09-04 14:14:03 +02:00
parent 43dd774840
commit e7ca7631fe
4 changed files with 311 additions and 50 deletions

View File

@ -142,29 +142,10 @@ void ViewProvider::setUpdatesEnabled (bool enable)
void ViewProvider::eventCallback(void * ud, SoEventCallback * node)
{
const SoEvent * ev = node->getEvent();
Gui::View3DInventorViewer* view = reinterpret_cast<Gui::View3DInventorViewer*>(node->getUserData());
Gui::View3DInventorViewer* viewer = reinterpret_cast<Gui::View3DInventorViewer*>(node->getUserData());
ViewProvider *self = reinterpret_cast<ViewProvider*>(ud);
assert(self);
// Calculate 3d point to the mouse position
SbVec3f point, norm;
point = view->getPointOnScreen(ev->getPosition());
norm = view->getViewDirection();
// for convenience make a pick ray action to get the (potentially) picked entity in the provider
//SoPickedPoint* Point = self->getPointOnRay(point,norm,*view);
SoSeparator* root = new SoSeparator;
root->ref();
root->addChild(view->getCamera());
root->addChild(self->pcRoot);
SoRayPickAction rp(view->getViewportRegion());
rp.setPoint(ev->getPosition());
rp.apply(root);
root->unref();
SoPickedPoint* pp = rp.getPickedPoint();
try {
// Keyboard events
if (ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
@ -192,12 +173,12 @@ void ViewProvider::eventCallback(void * ud, SoEventCallback * node)
const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE;
// call the virtual method
if (self->mouseButtonPressed(button,press,point,norm,pp))
if (self->mouseButtonPressed(button,press,ev->getPosition(),viewer))
node->setHandled();
}
// Mouse Movement handling
else if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) {
if (self->mouseMove(point,norm,pp))
if (self->mouseMove(ev->getPosition(),viewer))
node->setHandled();
}
}
@ -352,11 +333,28 @@ PyObject* ViewProvider::getPyObject()
return pyViewObject;
}
SoPickedPoint* ViewProvider::getPointOnRay(const SbVec3f& pos,const SbVec3f& dir, const View3DInventorViewer& viewer) const
SoPickedPoint* ViewProvider::getPointOnRay(const SbVec2s& pos, const View3DInventorViewer* viewer) const
{
// for convenience make a pick ray action to get the (potentially) picked entity in the provider
SoSeparator* root = new SoSeparator;
root->ref();
root->addChild(viewer->getCamera());
root->addChild(pcRoot);
SoRayPickAction rp(viewer->getViewportRegion());
rp.setPoint(pos);
rp.apply(root);
root->unref();
SoPickedPoint* pick = rp.getPickedPoint();
return (pick ? new SoPickedPoint(*pick) : 0);
}
SoPickedPoint* ViewProvider::getPointOnRay(const SbVec3f& pos,const SbVec3f& dir, const View3DInventorViewer* viewer) const
{
// Note: There seems to be a bug with setRay() which causes SoRayPickAction
// to fail to get intersections between the ray and a line
SoRayPickAction rp(viewer.getViewportRegion());
SoRayPickAction rp(viewer->getViewportRegion());
rp.setRay(pos,dir);
rp.apply(pcRoot);

View File

@ -30,6 +30,7 @@
#include <QIcon>
#include <boost/signals.hpp>
class SbVec2s;
class SbVec3f;
class SoNode;
class SoPath;
@ -220,11 +221,12 @@ public:
/// is called by the tree if the user double click on the object
virtual bool doubleClicked(void) { return false; }
/// is called when the provider is in edit and the mouse is moved
virtual bool mouseMove(const SbVec3f &pos, const SbVec3f &norm, const SoPickedPoint* pp)
virtual bool mouseMove(const SbVec2s &cursorPos,
View3DInventorViewer* viewer)
{ return false; }
/// is called when the Provider is in edit and the mouse is clicked
virtual bool mouseButtonPressed(int Button, bool pressed, const SbVec3f &pos,
const SbVec3f &norm, const SoPickedPoint* pp)
virtual bool mouseButtonPressed(int button, bool pressed, const SbVec2s &cursorPos,
const View3DInventorViewer* viewer)
{ return false; }
/// set up the context-menu with the supported edit modes
virtual void setupContextMenu(QMenu*, QObject*, const char*) {}
@ -264,8 +266,11 @@ protected:
void setDefaultMode(int);
//@}
/// Helper method to get picked entities while editing
SoPickedPoint* getPointOnRay(const SbVec3f& pos,const SbVec3f& dir,
const View3DInventorViewer& viewer) const;
SoPickedPoint* getPointOnRay(const SbVec2s& pos,
const View3DInventorViewer* viewer) const;
/// Helper method to get picked entities while editing
SoPickedPoint* getPointOnRay(const SbVec3f& pos, const SbVec3f& dir,
const View3DInventorViewer* viewer) const;
/// Reimplemented from subclass
void onChanged(const App::Property* prop);

View File

@ -79,6 +79,7 @@
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/Selection.h>
#include <Gui/Utilities.h>
#include <Gui/MainWindow.h>
#include <Gui/MenuManager.h>
#include <Gui/View3DInventor.h>
@ -115,6 +116,7 @@ SbColor ViewProviderSketch::SelectColor (0.11f,0.68f,0.11f); // #1CAD
// Variables for holding previous click
SbTime ViewProviderSketch::prvClickTime;
SbVec3f ViewProviderSketch::prvClickPoint;
SbVec2s ViewProviderSketch::prvCursorPos;
//**************************************************************************
// Edit data structure
@ -182,8 +184,8 @@ struct EditData {
SoCoordinate3 *RootCrossCoordinate;
SoCoordinate3 *EditCurvesCoordinate;
SoLineSet *CurveSet;
SoLineSet *EditCurveSet;
SoLineSet *RootCrossSet;
SoLineSet *EditCurveSet;
SoMarkerSet *PointSet;
SoText2 *textX;
@ -348,11 +350,17 @@ void ViewProviderSketch::getCoordsOnSketchPlane(double &u, double &v,const SbVec
v = S.y;
}
bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVec3f &point,
const SbVec3f &normal, const SoPickedPoint *pp)
bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVec2s &cursorPos,
const Gui::View3DInventorViewer *viewer)
{
assert(edit);
// Calculate 3d point to the mouse position
SbVec3f point = viewer->getPointOnScreen(cursorPos);
SbVec3f normal = viewer->getViewDirection();
SoPickedPoint *pp = this->getPointOnRay(cursorPos, viewer);
// Radius maximum to allow double click event
const int dblClickRadius = 5;
@ -375,11 +383,6 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
switch (Mode) {
case STATUS_NONE:{
bool done=false;
// Double click events variables
SbTime tmp = (SbTime::getTimeOfDay() - prvClickTime);
float dci = (float) QApplication::doubleClickInterval()/1000.0f;
float length = (point - prvClickPoint).length();
if (edit->PreselectPoint != -1) {
//Base::Console().Log("start dragging, point:%d\n",this->DragPoint);
Mode = STATUS_SELECT_Point;
@ -398,7 +401,13 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
done = true;
}
if (done && length < dblClickRadius && tmp.getValue() < dci) {
// Double click events variables
float dci = (float) QApplication::doubleClickInterval()/1000.0f;
if (done &&
(point - prvClickPoint).length() < dblClickRadius &&
(SbTime::getTimeOfDay() - prvClickTime).getValue() < dci) {
// Double Click Event Occured
editDoubleClicked();
// Reset Double Click Static Variables
@ -409,6 +418,9 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
} else {
prvClickTime = SbTime::getTimeOfDay();
prvClickPoint = point;
prvCursorPos = cursorPos;
if (!done)
Mode = STATUS_SKETCH_StartRubberBand;
}
return done;
@ -418,7 +430,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
default:
return false;
}
} else {
} else { // released
// Do things depending on the mode of the user interaction
switch (Mode) {
case STATUS_SELECT_Point:
@ -582,6 +594,16 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
}
Mode = STATUS_NONE;
return true;
case STATUS_SKETCH_StartRubberBand: // a single click happened, so clear selection
Mode = STATUS_NONE;
Gui::Selection().clearSelection();
return true;
case STATUS_SKETCH_UseRubberBand:
doBoxSelection(prvCursorPos, cursorPos, viewer);
// a redraw is required in order to clear the rubberband
draw(true);
Mode = STATUS_NONE;
return true;
case STATUS_SKETCH_UseHandler:
return edit->sketchHandler->releaseButton(Base::Vector2D(x,y));
case STATUS_NONE:
@ -740,12 +762,16 @@ void ViewProviderSketch::editDoubleClicked(void)
}
}
bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal, const SoPickedPoint *pp)
bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventorViewer *viewer)
{
if (!edit)
return false;
assert(edit);
// Calculate 3d point to the mouse position
SbVec3f point = viewer->getPointOnScreen(cursorPos);
SbVec3f normal = viewer->getViewDirection();
double x,y;
getCoordsOnSketchPlane(x,y,point,normal);
snapToGrid(x, y);
@ -753,6 +779,9 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal,
bool preselectChanged;
if (Mode!=STATUS_SKETCH_DragPoint && Mode!=STATUS_SKETCH_DragCurve &&
Mode!=STATUS_SKETCH_DragConstraint) {
SoPickedPoint *pp = this->getPointOnRay(cursorPos, viewer);
int PtIndex,GeoIndex,ConstrIndex,CrossIndex;
preselectChanged = detectPreselection(pp,PtIndex,GeoIndex,ConstrIndex,CrossIndex);
}
@ -858,6 +887,19 @@ bool ViewProviderSketch::mouseMove(const SbVec3f &point, const SbVec3f &normal,
this->updateColor();
}
return true;
case STATUS_SKETCH_StartRubberBand: {
Mode = STATUS_SKETCH_UseRubberBand;
return true;
}
case STATUS_SKETCH_UseRubberBand: {
// a redraw is required in order to clear any previous rubberband
draw(true);
viewer->drawRect(prvCursorPos.getValue()[0],
viewer->getGLWidget()->height() - prvCursorPos.getValue()[1],
cursorPos.getValue()[0],
viewer->getGLWidget()->height() - cursorPos.getValue()[1]);
return true;
}
default:
return false;
}
@ -1362,6 +1404,214 @@ bool ViewProviderSketch::detectPreselection(const SoPickedPoint *Point, int &PtI
return false;
}
void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s &endPos,
const Gui::View3DInventorViewer *viewer)
{
std::vector<SbVec2s> corners0;
corners0.push_back(startPos);
corners0.push_back(endPos);
std::vector<SbVec2f> corners = viewer->getGLPolygon(corners0);
// all calculations with polygon and proj are in dimensionless [0 1] screen coordinates
Base::Polygon2D polygon;
polygon.Add(Base::Vector2D(corners[0].getValue()[0], corners[0].getValue()[1]));
polygon.Add(Base::Vector2D(corners[0].getValue()[0], corners[1].getValue()[1]));
polygon.Add(Base::Vector2D(corners[1].getValue()[0], corners[1].getValue()[1]));
polygon.Add(Base::Vector2D(corners[1].getValue()[0], corners[0].getValue()[1]));
Gui::ViewVolumeProjection proj(viewer->getCamera()->getViewVolume());
App::Document* doc = App::GetApplication().getActiveDocument();
Sketcher::SketchObject *sketchObject = NULL;
if (doc)
sketchObject = dynamic_cast<Sketcher::SketchObject *>(doc->getActiveObject());
if (!sketchObject)
return;
Base::Placement Plm = sketchObject->Placement.getValue();
int intGeoCount = sketchObject->getHighestCurveIndex() + 1;
int extGeoCount = sketchObject->getExternalGeometryCount();
const std::vector<Part::Geometry *> geomlist = sketchObject->getCompleteGeometry(); // without memory allocation
assert(int(geomlist.size()) == extGeoCount + intGeoCount);
assert(int(geomlist.size()) >= 2);
Base::Vector3d pnt0, pnt1, pnt2, pnt;
int VertexId = -1; // the loop below should be in sync with the main loop in ViewProviderSketch::draw
// so that the vertex indices are calculated correctly
int GeoId = 0;
for (std::vector<Part::Geometry *>::const_iterator it = geomlist.begin(); it != geomlist.end()-2; ++it, ++GeoId) {
if (GeoId >= intGeoCount)
GeoId = -extGeoCount;
if ((*it)->getTypeId() == Part::GeomPoint::getClassTypeId()) {
// ----- Check if single point lies inside box selection -----/
const Part::GeomPoint *point = dynamic_cast<const Part::GeomPoint *>(*it);
Plm.multVec(point->getPoint(), pnt0);
pnt0 = proj(pnt0);
VertexId += 1;
if (polygon.Contains(Base::Vector2D(pnt0.x, pnt0.y))) {
std::stringstream ss;
ss << "Vertex" << VertexId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
} else if ((*it)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
// ----- Check if line segment lies inside box selection -----/
const Part::GeomLineSegment *lineSeg = dynamic_cast<const Part::GeomLineSegment *>(*it);
Plm.multVec(lineSeg->getStartPoint(), pnt1);
Plm.multVec(lineSeg->getEndPoint(), pnt2);
pnt1 = proj(pnt1);
pnt2 = proj(pnt2);
VertexId += 2;
bool pnt1Inside = polygon.Contains(Base::Vector2D(pnt1.x, pnt1.y));
bool pnt2Inside = polygon.Contains(Base::Vector2D(pnt2.x, pnt2.y));
if (pnt1Inside) {
std::stringstream ss;
ss << "Vertex" << VertexId - 1;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
if (pnt2Inside) {
std::stringstream ss;
ss << "Vertex" << VertexId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
if (pnt1Inside && pnt2Inside) {
std::stringstream ss;
ss << "Edge" << GeoId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
} else if ((*it)->getTypeId() == Part::GeomCircle::getClassTypeId()) {
// ----- Check if circle lies inside box selection -----/
const Part::GeomCircle *circle = dynamic_cast<const Part::GeomCircle *>(*it);
pnt0 = circle->getCenter();
VertexId += 1;
Plm.multVec(pnt0, pnt0);
pnt0 = proj(pnt0);
if (polygon.Contains(Base::Vector2D(pnt0.x, pnt0.y))) {
std::stringstream ss;
ss << "Vertex" << VertexId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
int countSegments = 12;
float segment = float(2 * M_PI) / countSegments;
// circumscribed polygon radius
float radius = float(circle->getRadius()) / cos(segment/2);
bool bpolyInside = true;
pnt0 = circle->getCenter();
float angle = 0.f;
for (int i = 0; i < countSegments; ++i, angle += segment) {
pnt = Base::Vector3d(pnt0.x + radius * cos(angle),
pnt0.y + radius * sin(angle),
0.f);
Plm.multVec(pnt, pnt);
pnt = proj(pnt);
if (!polygon.Contains(Base::Vector2D(pnt.x, pnt.y))) {
bpolyInside = false;
break;
}
}
if (bpolyInside) {
ss.clear();
ss.str("");
ss << "Edge" << GeoId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(),ss.str().c_str());
}
}
} else if ((*it)->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
// Check if arc lies inside box selection
const Part::GeomArcOfCircle *aoc = dynamic_cast<const Part::GeomArcOfCircle *>(*it);
pnt0 = aoc->getCenter();
pnt1 = aoc->getStartPoint();
pnt2 = aoc->getEndPoint();
VertexId += 3;
Plm.multVec(pnt0, pnt0);
Plm.multVec(pnt1, pnt1);
Plm.multVec(pnt2, pnt2);
pnt0 = proj(pnt0);
pnt1 = proj(pnt1);
pnt2 = proj(pnt2);
if (polygon.Contains(Base::Vector2D(pnt0.x, pnt0.y))) {
std::stringstream ss;
ss << "Vertex" << VertexId - 2;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
bool pnt1Inside = polygon.Contains(Base::Vector2D(pnt1.x, pnt1.y));
if (pnt1Inside) {
std::stringstream ss;
ss << "Vertex" << VertexId - 1;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
bool pnt2Inside = polygon.Contains(Base::Vector2D(pnt2.x, pnt2.y));
if (pnt2Inside) {
std::stringstream ss;
ss << "Vertex" << VertexId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
if (pnt1Inside && pnt2Inside) {
double startangle, endangle;
aoc->getRange(startangle, endangle);
if (startangle > endangle) // if arc is reversed
std::swap(startangle, endangle);
double range = endangle-startangle;
int countSegments = std::max(2, int(12.0 * range / (2 * M_PI)));
float segment = float(range) / countSegments;
// circumscribed polygon radius
float radius = float(aoc->getRadius()) / cos(segment/2);
bool bpolyInside = true;
pnt0 = aoc->getCenter();
float angle = float(startangle) + segment/2;
for (int i = 0; i < countSegments; ++i, angle += segment) {
pnt = Base::Vector3d(pnt0.x + radius * cos(angle),
pnt0.y + radius * sin(angle),
0.f);
Plm.multVec(pnt, pnt);
pnt = proj(pnt);
if (!polygon.Contains(Base::Vector2D(pnt.x, pnt.y))) {
bpolyInside = false;
break;
}
}
if (bpolyInside) {
std::stringstream ss;
ss << "Edge" << GeoId;
Gui::Selection().addSelection(doc->getName(), sketchObject->getNameInDocument(), ss.str().c_str());
}
}
} else if ((*it)->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
const Part::GeomBSplineCurve *spline = dynamic_cast<const Part::GeomBSplineCurve *>(*it);
std::vector<Base::Vector3d> poles = spline->getPoles();
VertexId += poles.size();
// TODO
}
}
}
void ViewProviderSketch::updateColor(void)
{
assert(edit);
@ -2543,7 +2793,7 @@ bool ViewProviderSketch::setEdit(int ModNum)
// clear the selection (convenience)
Gui::Selection().clearSelection();
// create the container for the addtitional edit data
// create the container for the additional edit data
assert(!edit);
edit = new EditData();
@ -2740,9 +2990,9 @@ void ViewProviderSketch::createEditInventorNodes(void)
cursorTextColor.setPackedValue((uint32_t)hGrp->GetUnsigned("CursorTextColor", cursorTextColor.getPackedValue()), transparency);
// stuff for the edit coordinates ++++++++++++++++++++++++++++++++++++++
SoMaterial *EditMaterials = new SoMaterial;
EditMaterials->diffuseColor = cursorTextColor;
edit->EditRoot->addChild(EditMaterials);
SoMaterial *CoordTextMaterials = new SoMaterial;
CoordTextMaterials->diffuseColor = cursorTextColor;
edit->EditRoot->addChild(CoordTextMaterials);
SoSeparator *Coordsep = new SoSeparator();
// no caching for fluctuand data structures
@ -2759,6 +3009,7 @@ void ViewProviderSketch::createEditInventorNodes(void)
edit->textX->justification = SoText2::LEFT;
edit->textX->string = "";
Coordsep->addChild(edit->textX);
edit->EditRoot->addChild(Coordsep);
// group node for the Constraint visual +++++++++++++++++++++++++++++++++++

View File

@ -112,7 +112,9 @@ public:
STATUS_SKETCH_DragPoint, /**< enum value while dragging a point. */
STATUS_SKETCH_DragCurve, /**< enum value while dragging a curve. */
STATUS_SKETCH_DragConstraint, /**< enum value while dragging a compatible constraint. */
STATUS_SKETCH_UseHandler /**< enum value a DrawSketchHandler is in control. */
STATUS_SKETCH_UseHandler, /**< enum value a DrawSketchHandler is in control. */
STATUS_SKETCH_StartRubberBand, /**< enum value for initiating a rubber band selection */
STATUS_SKETCH_UseRubberBand /**< enum value when making a rubber band selection *//**< enum value a DrawSketchHandler is in control. */
};
/// is called by GuiCommands to set the drawing mode
void setSketchMode(SketchMode mode) {Mode = mode;}
@ -124,10 +126,14 @@ public:
//@{
/// give the coordinates of a line on the sketch plane in sketcher (2D) coordinates
void getCoordsOnSketchPlane(double &u, double &v,const SbVec3f &point, const SbVec3f &normal);
/// helper to detect preselection
//bool handlePreselection(const SoPickedPoint *pp);
/// helper to detect preselection
bool detectPreselection(const SoPickedPoint *Point, int &PtIndex,int &GeoIndex, int &ConstrIndex, int &CrossIndex);
/// box selection method
void doBoxSelection(const SbVec2s &startPos, const SbVec2s &endPos,
const Gui::View3DInventorViewer *viewer);
/// helper change the color of the sketch according to selection and solver status
void updateColor(void);
/// get the pointer to the sketch document object
@ -161,12 +167,12 @@ public:
/// is called by the tree if the user double click on the object
virtual bool doubleClicked(void);
/// is called when the Provider is in edit and the mouse is moved
virtual bool mouseMove(const SbVec3f &pNear, const SbVec3f &pFar, const SoPickedPoint *pp);
virtual bool mouseMove(const SbVec2s &pos, Gui::View3DInventorViewer *viewer);
/// is called when the Provider is in edit and a key event ocours. Only ESC ends edit.
virtual bool keyPressed(bool pressed, int key);
/// is called when the Provider is in edit and the mouse is clicked
virtual bool mouseButtonPressed(int Button, bool pressed, const SbVec3f &point,
const SbVec3f &normal, const SoPickedPoint *pp);
virtual bool mouseButtonPressed(int Button, bool pressed, const SbVec2s &pos,
const Gui::View3DInventorViewer *viewer);
//@}
friend class DrawSketchHandler;
@ -229,6 +235,7 @@ protected:
static SbTime prvClickTime;
static SbVec3f prvClickPoint;
static SbVec2s prvCursorPos;
float zCross;
float zLines;