diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index 024d46c1e..8fdfd3a98 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -2215,6 +2215,62 @@ void StdCmdDemoMode::activated(int iMsg) dlg->show(); } +//=========================================================================== +// Part_Measure_Clear_All +//=========================================================================== + +DEF_STD_CMD(CmdViewMeasureClearAll); + +CmdViewMeasureClearAll::CmdViewMeasureClearAll() + : Command("View_Measure_Clear_All") +{ + sGroup = QT_TR_NOOP("Measure"); + sMenuText = QT_TR_NOOP("Clear All"); + sToolTipText = QT_TR_NOOP("Clear All"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Clear_All"; +} + +void CmdViewMeasureClearAll::activated(int iMsg) +{ + Gui::View3DInventor *view = dynamic_cast(Gui::Application::Instance-> + activeDocument()->getActiveView()); + if (!view) + return; + Gui::View3DInventorViewer *viewer = view->getViewer(); + if (!viewer) + return; + viewer->eraseAllDimensions(); +} + +//=========================================================================== +// Part_Measure_Toggle_All +//=========================================================================== + +DEF_STD_CMD(CmdViewMeasureToggleAll); + +CmdViewMeasureToggleAll::CmdViewMeasureToggleAll() + : Command("View_Measure_Toggle_All") +{ + sGroup = QT_TR_NOOP("Measure"); + sMenuText = QT_TR_NOOP("Toggle All"); + sToolTipText = QT_TR_NOOP("Toggle All"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Toggle_All"; +} + +void CmdViewMeasureToggleAll::activated(int iMsg) +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("DimensionsVisible", true); + if (visibility) + group->SetBool("DimensionsVisible", false); + else + group->SetBool("DimensionsVisible", true); +} //=========================================================================== // Instantiation @@ -2280,6 +2336,8 @@ void CreateViewStdCommands(void) rcCmdMgr.addCommand(new StdCmdDemoMode()); rcCmdMgr.addCommand(new StdCmdToggleNavigation()); rcCmdMgr.addCommand(new StdCmdAxisCross()); + rcCmdMgr.addCommand(new CmdViewMeasureClearAll()); + rcCmdMgr.addCommand(new CmdViewMeasureToggleAll()); } } // namespace Gui diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 01ed3653c..890cada0e 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -152,6 +152,9 @@ View3DInventor::View3DInventor(Gui::Document* pcDocument, QWidget* parent, Qt::W OnChange(*hGrp,"OrbitStyle"); OnChange(*hGrp,"Sensitivity"); OnChange(*hGrp,"ResetCursorPosition"); + OnChange(*hGrp,"DimensionsVisible"); + OnChange(*hGrp,"Dimensions3dVisible"); + OnChange(*hGrp,"DimensionsDeltaVisible"); stopSpinTimer = new QTimer(this); connect(stopSpinTimer, SIGNAL(timeout()), this, SLOT(stopAnimating())); @@ -360,7 +363,28 @@ void View3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::M else _viewer->setCameraType(SoPerspectiveCamera::getClassTypeId()); } - else { + else if (strcmp(Reason, "DimensionsVisible") == 0) + { + if (rGrp.GetBool("DimensionsVisible", true)) + _viewer->turnAllDimensionsOn(); + else + _viewer->turnAllDimensionsOff(); + } + else if (strcmp(Reason, "Dimensions3dVisible") == 0) + { + if (rGrp.GetBool("Dimensions3dVisible", true)) + _viewer->turn3dDimensionsOn(); + else + _viewer->turn3dDimensionsOff(); + } + else if (strcmp(Reason, "DimensionsDeltaVisible") == 0) + { + if (rGrp.GetBool("DimensionsDeltaVisible", true)) + _viewer->turnDeltaDimensionsOn(); + else + _viewer->turnDeltaDimensionsOff(); + } + else{ unsigned long col1 = rGrp.GetUnsigned("BackgroundColor",3940932863UL); unsigned long col2 = rGrp.GetUnsigned("BackgroundColor2",859006463UL); // default color (dark blue) unsigned long col3 = rGrp.GetUnsigned("BackgroundColor3",2880160255UL); // default color (blue/grey) diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 02d410251..6aa1192d1 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -250,6 +250,11 @@ View3DInventorViewer::View3DInventorViewer (QWidget *parent, const char *name, pEventCallback->ref(); pcViewProviderRoot->addChild(pEventCallback); pEventCallback->addEventCallback(SoEvent::getClassTypeId(), handleEventCB, this); + + dimensionRoot = new SoSwitch(SO_SWITCH_NONE); + pcViewProviderRoot->addChild(dimensionRoot); + dimensionRoot->addChild(new SoSwitch()); //first one will be for the 3d dimensions. + dimensionRoot->addChild(new SoSwitch()); //second one for the delta dimensions. // This is a callback node that logs all action that traverse the Inventor tree. #if defined (FC_DEBUG) && defined(FC_LOGGING_CB) @@ -2182,3 +2187,49 @@ std::vector View3DInventorViewer::getViewProvidersOfType(const Ba } return views; } + +void View3DInventorViewer::turnAllDimensionsOn() +{ + dimensionRoot->whichChild = SO_SWITCH_ALL; +} + +void View3DInventorViewer::turnAllDimensionsOff() +{ + dimensionRoot->whichChild = SO_SWITCH_NONE; +} + +void View3DInventorViewer::eraseAllDimensions() +{ + static_cast(dimensionRoot->getChild(0))->removeAllChildren(); + static_cast(dimensionRoot->getChild(1))->removeAllChildren(); +} + +void View3DInventorViewer::turn3dDimensionsOn() +{ + static_cast(dimensionRoot->getChild(0))->whichChild = SO_SWITCH_ALL; +} + +void View3DInventorViewer::turn3dDimensionsOff() +{ + static_cast(dimensionRoot->getChild(0))->whichChild = SO_SWITCH_NONE; +} + +void View3DInventorViewer::addDimension3d(SoNode *node) +{ + static_cast(dimensionRoot->getChild(0))->addChild(node); +} + +void View3DInventorViewer::addDimensionDelta(SoNode *node) +{ + static_cast(dimensionRoot->getChild(1))->addChild(node); +} + +void View3DInventorViewer::turnDeltaDimensionsOn() +{ + static_cast(dimensionRoot->getChild(1))->whichChild = SO_SWITCH_ALL; +} + +void View3DInventorViewer::turnDeltaDimensionsOff() +{ + static_cast(dimensionRoot->getChild(1))->whichChild = SO_SWITCH_NONE; +} diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index d37fc9101..1728e381f 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -112,8 +112,8 @@ public: SbBool isBacklight(void) const; void setSceneGraph (SoNode *root); - void setAnimationEnabled(const SbBool enable); - SbBool isAnimationEnabled(void) const; + void setAnimationEnabled(const SbBool enable); + SbBool isAnimationEnabled(void) const; void setPopupMenuEnabled(const SbBool on); SbBool isPopupMenuEnabled(void) const; @@ -121,16 +121,16 @@ public: void startAnimating(const SbVec3f& axis, float velocity); void stopAnimating(void); SbBool isAnimating(void) const; - - void setFeedbackVisibility(const SbBool enable); - SbBool isFeedbackVisible(void) const; - - void setFeedbackSize(const int size); - int getFeedbackSize(void) const; - void setRenderFramebuffer(const SbBool enable); - SbBool isRenderFramebuffer() const; - void renderToFramebuffer(QGLFramebufferObject*); + void setFeedbackVisibility(const SbBool enable); + SbBool isFeedbackVisible(void) const; + + void setFeedbackSize(const int size); + int getFeedbackSize(void) const; + + void setRenderFramebuffer(const SbBool enable); + SbBool isRenderFramebuffer() const; + void renderToFramebuffer(QGLFramebufferObject*); virtual void setViewing(SbBool enable); virtual void setCursorEnabled(SbBool enable); @@ -193,8 +193,8 @@ public: void setEditingCursor (const QCursor& cursor); void setRedirectToSceneGraph(SbBool redirect) { this->redirected = redirect; } SbBool isRedirectedToSceneGraph() const { return this->redirected; } - void setRedirectToSceneGraphEnabled(SbBool enable) { this->allowredir = enable; } - SbBool isRedirectToSceneGraphEnabled(void) const { return this->allowredir; } + void setRedirectToSceneGraphEnabled(SbBool enable) { this->allowredir = enable; } + SbBool isRedirectToSceneGraphEnabled(void) const { return this->allowredir; } //@} /** @name Pick actions */ @@ -241,6 +241,23 @@ public: /** Project the given normalized 2d point onto the far plane */ SbVec3f projectOnFarPlane(const SbVec2f&) const; //@} + + /** @name Dimension controls + * the "turn*" functions are wired up to parameter groups through view3dinventor. + * don't call them directly. instead set the parameter groups. + * @see TaskDimension + */ + //@{ + void turnAllDimensionsOn(); + void turnAllDimensionsOff(); + void turn3dDimensionsOn(); + void turn3dDimensionsOff(); + void turnDeltaDimensionsOn(); + void turnDeltaDimensionsOff(); + void eraseAllDimensions(); + void addDimension3d(SoNode *node); + void addDimensionDelta(SoNode *node); + //@} /** * Set the camera's orientation. If isAnimationEnabled() returns @@ -320,7 +337,6 @@ private: SoFCBackgroundGradient *pcBackGround; SoSeparator * backgroundroot; SoSeparator * foregroundroot; - SoRotationXYZ * arrowrotation; SoDirectionalLight* backlight; SoSeparator * pcViewProviderRoot; @@ -328,6 +344,7 @@ private: NavigationStyle* navigation; SoFCUnifiedSelection* selectionRoot; QGLFramebufferObject* framebuffer; + SoSwitch *dimensionRoot; // small axis cross in the corner SbBool axiscrossEnabled; diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index 1e9ba4296..b8cb0cb40 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -399,7 +399,11 @@ void StdWorkbench::setupContextMenu(const char* recipient, MenuItem* item) const << "Std_ViewRear" << "Std_ViewBottom" << "Std_ViewLeft" << "Separator" << "Std_ViewRotateLeft" << "Std_ViewRotateRight"; - *item << "Std_ViewFitAll" << "Std_ViewFitSelection" << StdViews + MenuItem *measure = new MenuItem(); + measure->setCommand("Measure"); + *measure << "View_Measure_Toggle_All" << "View_Measure_Clear_All"; + + *item << "Std_ViewFitAll" << "Std_ViewFitSelection" << StdViews << measure << "Separator" << "Std_ViewDockUndockFullscreen"; if (Gui::Selection().countObjectsOfType(App::DocumentObject::getClassTypeId()) > 0 ) diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 1a9848968..2a74641e6 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -53,7 +53,7 @@ #include "ViewProviderPrism.h" #include "ViewProviderSpline.h" #include "ViewProviderRegularPolygon.h" - +#include "TaskDimension.h" #include "DlgSettingsGeneral.h" #include "DlgSettingsObjectColor.h" #include "DlgSettings3DViewPartImp.h" @@ -146,6 +146,9 @@ void PartGuiExport initPartGui() PartGui::ViewProviderConeParametric ::init(); PartGui::ViewProviderTorusParametric ::init(); PartGui::ViewProviderRuledSurface ::init(); + PartGui::DimensionLinear ::initClass(); + PartGui::DimensionAngular ::initClass(); + PartGui::ArcEngine ::initClass(); PartGui::Workbench ::init(); diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index d301196b0..245e005d8 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -45,6 +45,7 @@ set(PartGui_MOC_HDRS TaskOffset.h TaskSweep.h TaskThickness.h + TaskDimension.h TaskCheckGeometry.h ) fc_wrap_cpp(PartGui_MOC_SRCS ${PartGui_MOC_HDRS}) @@ -208,6 +209,8 @@ SET(PartGui_SRCS TaskSweep.ui TaskThickness.cpp TaskThickness.h + TaskDimension.cpp + TaskDimension.h TaskCheckGeometry.cpp TaskCheckGeometry.h ) diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index c51b9b87c..65df59e90 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -63,6 +63,7 @@ #include "TaskShapeBuilder.h" #include "TaskLoft.h" #include "TaskSweep.h" +#include "TaskDimension.h" #include "TaskCheckGeometry.h" @@ -1495,6 +1496,179 @@ bool CmdColorPerFace::isActive(void) return (hasActiveDocument() && !Gui::Control().activeDialog() && objectSelected); } +//=========================================================================== +// Part_Measure_Linear +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureLinear); + +CmdMeasureLinear::CmdMeasureLinear() + : Command("Part_Measure_Linear") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Measure Linear"); + sToolTipText = QT_TR_NOOP("Measure Linear"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Linear"; +} + +void CmdMeasureLinear::activated(int iMsg) +{ + PartGui::goDimensionLinearRoot(); +} + +bool CmdMeasureLinear::isActive(void) +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Measure_Angular +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureAngular); + +CmdMeasureAngular::CmdMeasureAngular() + : Command("Part_Measure_Angular") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Measure Angular"); + sToolTipText = QT_TR_NOOP("Measure Angular"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Angular"; +} + +void CmdMeasureAngular::activated(int iMsg) +{ + PartGui::goDimensionAngularRoot(); +} + +bool CmdMeasureAngular::isActive(void) +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Measure_Clear_All +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureClearAll); + +CmdMeasureClearAll::CmdMeasureClearAll() + : Command("Part_Measure_Clear_All") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Clear All"); + sToolTipText = QT_TR_NOOP("Clear All"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Clear_All"; +} + +void CmdMeasureClearAll::activated(int iMsg) +{ + PartGui::eraseAllDimensions(); +} + +bool CmdMeasureClearAll::isActive(void) +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Measure_Toggle_All +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureToggleAll); + +CmdMeasureToggleAll::CmdMeasureToggleAll() + : Command("Part_Measure_Toggle_All") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Toggle All"); + sToolTipText = QT_TR_NOOP("Toggle All"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Toggle_All"; +} + +void CmdMeasureToggleAll::activated(int iMsg) +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("DimensionsVisible", true); + if (visibility) + group->SetBool("DimensionsVisible", false); + else + group->SetBool("DimensionsVisible", true); +} + +bool CmdMeasureToggleAll::isActive(void) +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Measure_Toggle_3d +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureToggle3d); + +CmdMeasureToggle3d::CmdMeasureToggle3d() + : Command("Part_Measure_Toggle_3d") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Toggle 3d"); + sToolTipText = QT_TR_NOOP("Toggle 3d"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Toggle_3d"; +} + +void CmdMeasureToggle3d::activated(int iMsg) +{ + PartGui::toggle3d(); +} + +bool CmdMeasureToggle3d::isActive(void) +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Measure_Toggle_Delta +//=========================================================================== + +DEF_STD_CMD_A(CmdMeasureToggleDelta); + +CmdMeasureToggleDelta::CmdMeasureToggleDelta() + : Command("Part_Measure_Toggle_Delta") +{ + sAppModule = "Part"; + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Toggle Delta"); + sToolTipText = QT_TR_NOOP("Toggle Delta"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Part_Measure_Toggle_Delta"; +} + +void CmdMeasureToggleDelta::activated(int iMsg) +{ + PartGui::toggleDelta(); +} + +bool CmdMeasureToggleDelta::isActive(void) +{ + return hasActiveDocument(); +} void CreatePartCommands(void) { @@ -1531,4 +1705,10 @@ void CreatePartCommands(void) rcCmdMgr.addCommand(new CmdPartThickness()); rcCmdMgr.addCommand(new CmdCheckGeometry()); rcCmdMgr.addCommand(new CmdColorPerFace()); + rcCmdMgr.addCommand(new CmdMeasureLinear()); + rcCmdMgr.addCommand(new CmdMeasureAngular()); + rcCmdMgr.addCommand(new CmdMeasureClearAll()); + rcCmdMgr.addCommand(new CmdMeasureToggleAll()); + rcCmdMgr.addCommand(new CmdMeasureToggle3d()); + rcCmdMgr.addCommand(new CmdMeasureToggleDelta()); } diff --git a/src/Mod/Part/Gui/Resources/Part.qrc b/src/Mod/Part/Gui/Resources/Part.qrc index 1202a86c9..763035a67 100644 --- a/src/Mod/Part/Gui/Resources/Part.qrc +++ b/src/Mod/Part/Gui/Resources/Part.qrc @@ -42,6 +42,14 @@ icons/Part_Point_Parametric.svg icons/Part_Polygon_Parametric.svg icons/Part_Spline_Parametric.svg + icons/Part_Measure_Linear.svg + icons/Part_Measure_Angular.svg + icons/Part_Measure_Clear_All.svg + icons/Part_Measure_Toggle_All.svg + icons/Part_Measure_Toggle_3d.svg + icons/Part_Measure_Toggle_Delta.svg + icons/Part_Measure_Step_Active.svg + icons/Part_Measure_Step_Done.svg icons/Tree_Part_Box_Parametric.svg icons/Tree_Part_Cylinder_Parametric.svg icons/Tree_Part_Cone_Parametric.svg diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Angular.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Angular.svg new file mode 100644 index 000000000..b18a93812 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Angular.svg @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Clear_All.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Clear_All.svg new file mode 100644 index 000000000..5915af9ec --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Clear_All.svg @@ -0,0 +1,583 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Linear.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Linear.svg new file mode 100644 index 000000000..ee8ce7208 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Linear.svg @@ -0,0 +1,448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Active.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Active.svg new file mode 100644 index 000000000..14bd3a549 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Active.svg @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Done.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Done.svg new file mode 100644 index 000000000..0bcb47402 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Step_Done.svg @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_3d.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_3d.svg new file mode 100644 index 000000000..b972acc8b --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_3d.svg @@ -0,0 +1,454 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_All.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_All.svg new file mode 100644 index 000000000..13584665f --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_All.svg @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_Delta.svg b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_Delta.svg new file mode 100644 index 000000000..b7d5d7e84 --- /dev/null +++ b/src/Mod/Part/Gui/Resources/icons/Part_Measure_Toggle_Delta.svg @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Part/Gui/TaskDimension.cpp b/src/Mod/Part/Gui/TaskDimension.cpp new file mode 100644 index 000000000..6806f668b --- /dev/null +++ b/src/Mod/Part/Gui/TaskDimension.cpp @@ -0,0 +1,1678 @@ +/*************************************************************************** + * Copyright (c) 2013 Thomas Anderson * + * * + * 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" + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../App/PartFeature.h" +#include +#include +#include +#include +#include +#include +#include + +#include "TaskDimension.h" + +bool PartGui::getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub) +{ + App::Document *docPointer = App::GetApplication().getDocument(doc.c_str()); + if (!docPointer) + return false; + App::DocumentObject *objectPointer = docPointer->getObject(object.c_str()); + if (!objectPointer) + return false; + Part::Feature *feature = dynamic_cast(objectPointer); + if (!feature) + return false; + shapeOut = feature->Shape.getValue(); + if (sub.size() > 0) + shapeOut = feature->Shape.getShape().getSubShape(sub.c_str()); + if (shapeOut.IsNull()) + return false; + return true; +} + +bool PartGui::evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2) +{ + std::vector selections = Gui::Selection().getSelection(); + if (selections.size() != 2) + return false; + std::vector::iterator it; + std::vector shapes; + + for (it = selections.begin(); it != selections.end(); ++it) + { + Part::Feature *feature = dynamic_cast((*it).pObject); + if (!feature) + break; + TopoDS_Shape shape = feature->Shape.getValue(); + if (strlen((*it).SubName) > 0) + shape = feature->Shape.getShape().getSubShape((*it).SubName); + if (shape.IsNull()) + break; + shapes.push_back(shape); + } + + if (shapes.size() != 2) + return false; + + shape1 = shapes.front(); + shape2 = shapes.back(); + + return true; +} + +void PartGui::goDimensionLinearRoot() +{ + PartGui::ensureSomeDimensionVisible(); + + TopoDS_Shape shape1, shape2; + if(evaluateLinearPreSelection(shape1, shape2)) + { + goDimensionLinearNoTask(shape1, shape2); + Gui::Selection().clearSelection(); + } + else + { + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (!dlg) + { + Gui::Selection().clearSelection(); + dlg = new PartGui::TaskMeasureLinear(); + } + Gui::Control().showDialog(dlg); + } +} + +void PartGui::goDimensionLinearNoTask(const TopoDS_Shape &shape1, const TopoDS_Shape &shape2) +{ + //Warning: BRepExtrema_DistShapeShape solution array is NOT 0 based. + BRepExtrema_DistShapeShape measure(shape1, shape2); + if (!measure.IsDone() || measure.NbSolution() < 1) + return; + + dumpLinearResults(measure); + addLinearDimensions(measure); + + //if we ever make this a class add viewer to member. + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; +} + +void PartGui::dumpLinearResults(const BRepExtrema_DistShapeShape &measure) +{ + std::ostringstream out; + //switch to initializer list when switch to c++11 + std::vector typeNames; + typeNames.resize(3); + typeNames[0] = "Vertex"; + typeNames[1] = "Edge"; + typeNames[2] = "Face"; + + Base::Quantity quantity(measure.Value(), Base::Unit::Length); + out << std::endl<< std::setprecision(std::numeric_limits::digits10 + 1) << "distance = " << + measure.Value() << "mm unit distance = " << quantity.getUserString().toUtf8().constData() << std::endl << + "solution count: " << measure.NbSolution() << std::endl; + + for (int index = 1; index < measure.NbSolution() + 1; ++index) //not zero based. + { + gp_Pnt point1 = measure.PointOnShape1(index); + gp_Pnt point2 = measure.PointOnShape2(index); + out << " solution " << index << ":" << std::endl << std::setprecision(std::numeric_limits::digits10 + 1) << + " point1 " << point1.X() << " " << point1.Y() << " " << point1.Z() << std::endl << + " point2 " << point2.X() << " " << point2.Y() << " " << point2.Z() << std::endl << + " DeltaX " << fabs(point2.X() - point1.X()) << std::endl << + " DeltaY " << fabs(point2.Y() - point1.Y()) << std::endl << + " DeltaZ " << fabs(point2.Z() - point1.Z()) << std::endl << + " shape type on object1 is: " << typeNames.at(measure.SupportTypeShape1(index)) << std::endl << + " shape type on object2 is: " << typeNames.at(measure.SupportTypeShape2(index)) << std::endl; + } + out << std::endl; + Base::Console().Message(out.str().c_str()); +} + +Gui::View3DInventorViewer * PartGui::getViewer() +{ + Gui::View3DInventor *view = dynamic_cast(Gui::Application::Instance-> + activeDocument()->getActiveView()); + if (!view) + return 0; + Gui::View3DInventorViewer *viewer = view->getViewer(); + if (!viewer) + return 0; + return viewer; +} + +void PartGui::addLinearDimensions(const BRepExtrema_DistShapeShape &measure) +{ + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; + for (int index = 1; index < measure.NbSolution() + 1; ++index) + { + gp_Pnt point1 = measure.PointOnShape1(index); + gp_Pnt point2 = measure.PointOnShape2(index); + viewer->addDimension3d(createLinearDimension(point1, point2, SbColor(1.0, 0.0, 0.0))); + + //create deltas. point1 will always be the same. + gp_Pnt temp = point1; + gp_Pnt lastTemp = temp; + temp.SetX(point2.X()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + lastTemp = temp; + temp.SetY(point2.Y()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + lastTemp = temp; + temp.SetZ(point2.Z()); + viewer->addDimensionDelta(createLinearDimension(lastTemp, temp, SbColor(0.0, 1.0, 0.0))); + } +} + +SoNode* PartGui::createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color) +{ + SbVec3f vec1(point1.X(), point1.Y(), point1.Z()); + SbVec3f vec2(point2.X(), point2.Y(), point2.Z()); + if ((vec2-vec1).length() < FLT_EPSILON) + return new SoSeparator(); //empty object. + PartGui::DimensionLinear *dimension = new PartGui::DimensionLinear(); + dimension->point1.setValue(vec1); + dimension->point2.setValue(vec2); + + Base::Quantity quantity(static_cast((vec2-vec1).length()), Base::Unit::Length); + dimension->text.setValue(quantity.getUserString().toUtf8().constData()); + + dimension->dColor.setValue(color); + return dimension; +} + +void PartGui::eraseAllDimensions() +{ + Gui::View3DInventor *view = dynamic_cast(Gui::Application::Instance-> + activeDocument()->getActiveView()); + if (!view) + return; + Gui::View3DInventorViewer *viewer = view->getViewer(); + if (!viewer) + return; + viewer->eraseAllDimensions(); +} + +void PartGui::toggle3d() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("Dimensions3dVisible", true); + if (visibility) + group->SetBool("Dimensions3dVisible", false); + else + group->SetBool("Dimensions3dVisible", true); +} + +void PartGui::toggleDelta() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibility = group->GetBool("DimensionsDeltaVisible", true); + if (visibility) + group->SetBool("DimensionsDeltaVisible", false); + else + group->SetBool("DimensionsDeltaVisible", true); +} + +void PartGui::ensureSomeDimensionVisible() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibilityAll = group->GetBool("DimensionsVisible", true); + if (!visibilityAll) + group->SetBool("DimensionsVisible", true); + bool visibility3d = group->GetBool("Dimensions3dVisible", true); + bool visibilityDelta = group->GetBool("DimensionsDeltaVisible", true); + + if (!visibility3d && !visibilityDelta) //both turned off. + group->SetBool("Dimensions3dVisible", true); //turn on 3d, so something is visible. +} + +void PartGui::ensure3dDimensionVisible() +{ + ParameterGrp::handle group = App::GetApplication().GetUserParameter(). + GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("View"); + bool visibilityAll = group->GetBool("DimensionsVisible", true); + if (!visibilityAll) + group->SetBool("DimensionsVisible", true); + bool visibility3d = group->GetBool("Dimensions3dVisible", true); + + if (!visibility3d) //both turned off. + group->SetBool("Dimensions3dVisible", true); //turn on 3d, so something is visible. +} + + +SO_KIT_SOURCE(PartGui::DimensionLinear); + +void PartGui::DimensionLinear::initClass() +{ + SO_KIT_INIT_CLASS(DimensionLinear, SoSeparatorKit, "SeparatorKit"); +} + +PartGui::DimensionLinear::DimensionLinear() +{ + SO_KIT_CONSTRUCTOR(PartGui::DimensionLinear); + + SO_KIT_ADD_CATALOG_ENTRY(transformation, SoTransform, TRUE, topSeparator,"" , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(annotate, SoAnnotation, TRUE, topSeparator,"" , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(leftArrow, SoShapeKit, TRUE, topSeparator,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(rightArrow, SoShapeKit, TRUE, topSeparator,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(line, SoShapeKit, TRUE, annotate,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(textSep, SoSeparator, TRUE, annotate,"" ,TRUE); + + SO_KIT_INIT_INSTANCE(); + + SO_NODE_ADD_FIELD(rotate, (1.0, 0.0, 0.0, 0.0));//postion orientation of the dimension. + SO_NODE_ADD_FIELD(length, (1.0));//turns into dimension length + SO_NODE_ADD_FIELD(origin, (0.0, 0.0, 0.0));//static + SO_NODE_ADD_FIELD(text, ("test"));//dimension text + SO_NODE_ADD_FIELD(dColor, (1.0, 0.0, 0.0));//dimension color. + + point1.setValue(SbVec3f(0.0, 0.0, 0.0)); + point2.setValue(SbVec3f(1.0, 0.0, 0.0)); + + setupDimension(); +} + +PartGui::DimensionLinear::~DimensionLinear() +{ + +} + +SbBool PartGui::DimensionLinear::affectsState() const +{ + return FALSE; +} + +void PartGui::DimensionLinear::setupDimension() +{ + //transformation + SoTransform *trans = static_cast(getPart("transformation", TRUE)); + trans->translation.connectFrom(&point1); + //build engine for vector subtraction and length. + SoCalculator *hyp = new SoCalculator(); + hyp->A.connectFrom(&point1); + hyp->B.connectFrom(&point2); + hyp->expression.set1Value(0, "oA = B-A"); + hyp->expression.set1Value(1, "oB = normalize(oA)"); + hyp->expression.set1Value(2, "oa = length(oA)"); + length.connectFrom(&hyp->oa); + //build engine for rotation. + SoComposeRotationFromTo *rotationEngine = new SoComposeRotationFromTo(); + rotationEngine->from.setValue(SbVec3f(1.0, 0.0, 0.0)); + rotationEngine->to.connectFrom(&hyp->oB); + trans->rotation.connectFrom(&rotationEngine->rotation); + + //color + SoMaterial *material = new SoMaterial; + material->diffuseColor.connectFrom(&dColor); + + //dimension arrows. + SoCone *cone = new SoCone(); + cone->bottomRadius.setValue(0.25); + cone->height.setValue(0.5); + + setPart("leftArrow.shape", cone); + set("leftArrow.transform", "rotation 0.0 0.0 1.0 1.5707963"); + set("leftArrow.transform", "translation 0.25 0.0 0.0"); //half cone height. + setPart("rightArrow.shape", cone); + set("rightArrow.transform", "rotation 0.0 0.0 -1.0 1.5707963"); //no constant for PI. + //have use local here to do the offset because the main is wired up to length of dimension. + set("rightArrow.localTransform", "translation 0.0 -0.25 0.0"); //half cone height. + + SoTransform *transform = static_cast(getPart("rightArrow.transform", FALSE)); + if (!transform) + return;//what to do here? + SoComposeVec3f *vec = new SoComposeVec3f; + vec->x.connectFrom(&length); + vec->y.setValue(0.0); + vec->z.setValue(0.0); + transform->translation.connectFrom(&vec->vector); + + setPart("leftArrow.material", material); + setPart("rightArrow.material", material); + + //line + SoConcatenate *catEngine = new SoConcatenate(SoMFVec3f::getClassTypeId()); + //don't know how to get around having this dummy origin. cat engine wants to connectfrom? + catEngine->input[0]->connectFrom(&origin); + catEngine->input[1]->connectFrom(&vec->vector); + + SoVertexProperty *lineVerts = new SoVertexProperty; + lineVerts->vertex.connectFrom(catEngine->output); + + int lineVertexMap[] = {0, 1}; + int lineVertexMapSize(sizeof(lineVertexMap)/sizeof(int)); + SoIndexedLineSet *line = new SoIndexedLineSet; + line->vertexProperty = lineVerts; + line->coordIndex.setValues(0, lineVertexMapSize, lineVertexMap); + + setPart("line.shape", line); + setPart("line.material", material); + + //text + SoSeparator *textSep = static_cast(getPart("textSep", TRUE)); + if (!textSep) + return; + + textSep->addChild(material); + + SoCalculator *textVecCalc = new SoCalculator(); + textVecCalc->A.connectFrom(&vec->vector); + textVecCalc->B.set1Value(0, 0.0, 0.250, 0.0); + textVecCalc->expression.set1Value(0, "oA = (A / 2) + B"); + + SoTransform *textTransform = new SoTransform(); + textTransform->translation.connectFrom(&textVecCalc->oA); + textSep->addChild(textTransform); + + SoFont *fontNode = new SoFont(); + fontNode->name.setValue("defaultFont"); + fontNode->size.setValue(30); + textSep->addChild(fontNode); + + SoText2 *textNode = new SoText2(); + textNode->justification = SoText2::CENTER; + textNode->string.connectFrom(&text); + textSep->addChild(textNode); + + //this prevents the 2d text from screwing up the bounding box for a viewall + SoResetTransform *rTrans = new SoResetTransform; + rTrans->whatToReset = SoResetTransform::BBOX; + textSep->addChild(rTrans); +} + +PartGui::TaskMeasureLinear::TaskMeasureLinear(): selections1(), selections2(), buttonSelectedIndex(0) +{ + setUpGui(); +} + +PartGui::TaskMeasureLinear::~TaskMeasureLinear() +{ + Gui::Selection().clearSelection(); +} + +void PartGui::TaskMeasureLinear::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (buttonSelectedIndex == 0) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + selections1.selections.clear();//we only want one item. + selections1.selections.push_back(newSelection); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + } + if (buttonSelectedIndex == 1) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + selections2.selections.clear();//we only want one item. + selections2.selections.push_back(newSelection); + buildDimension(); + clearSelectionStrings(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + } +} + +void PartGui::TaskMeasureLinear::selectionClearDelayedSlot() +{ + //hack. + //clearing selections are not working as I hoped. Apparently the observer callback gets called + //before the actual selection takes place. Resulting in selections being left. this addresses this + //by being called from the event loop. + this->blockConnection(true); + Gui::Selection().clearSelection(); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::buildDimension() +{ + if(selections1.selections.size() != 1 || selections2.selections.size() != 1) + return; + + DimSelections::DimSelection current1 = selections1.selections.at(0); + DimSelections::DimSelection current2 = selections2.selections.at(0); + + TopoDS_Shape shape1, shape2; + if (!getShapeFromStrings(shape1, current1.documentName, current1.objectName, current1.subObjectName)) + { + Base::Console().Message("\nFailed to get shape\n\n"); + return; + } + if (!getShapeFromStrings(shape2, current2.documentName, current2.objectName, current2.subObjectName)) + { + Base::Console().Message("\nFailed to get shape\n\n"); + return; + } + goDimensionLinearNoTask(shape1, shape2); +} + +void PartGui::TaskMeasureLinear::clearSelectionStrings() +{ + selections1.selections.clear(); + selections2.selections.clear(); +} + +void PartGui::TaskMeasureLinear::setUpGui() +{ + QPixmap mainIcon = Gui::BitmapFactory().pixmap("Part_Measure_Linear"); + + Gui::TaskView::TaskBox* selectionTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Selections"), false, 0); + QVBoxLayout *selectionLayout = new QVBoxLayout(); + stepped = new SteppedSelection(2, selectionTaskBox); + selectionLayout->addWidget(stepped); + selectionTaskBox->groupLayout()->addLayout(selectionLayout); + + Gui::TaskView::TaskBox* controlTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Control"), false, 0); + QVBoxLayout *controlLayout = new QVBoxLayout(); + + QHBoxLayout *resetLayout = new QHBoxLayout(); + resetLayout->addSpacing(5); //hack for alignment. + QPushButton *resetButton = new QPushButton(mainIcon, QObject::tr("Reset Dialog"), controlTaskBox); + resetLayout->addWidget(resetButton); + resetLayout->addStretch(); + controlLayout->addLayout(resetLayout); + QObject::connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); + + DimensionControl *control = new DimensionControl(controlTaskBox); + controlLayout->addWidget(control); + controlTaskBox->groupLayout()->addLayout(controlLayout); + + this->setButtonPosition(TaskDialog::South); + Content.push_back(selectionTaskBox); + Content.push_back(controlTaskBox); + + stepped->getButton(0)->setChecked(true);//before wired up. + QObject::connect(stepped->getButton(0), SIGNAL(toggled(bool)), this, SLOT(selection1Slot(bool))); + QObject::connect(stepped->getButton(1), SIGNAL(toggled(bool)), this, SLOT(selection2Slot(bool))); +} + +void PartGui::TaskMeasureLinear::selection1Slot(bool checked) +{ + if (!checked) + { + if (selections1.selections.size() > 0) + stepped->setIconDone(0); + return; + } + buttonSelectedIndex = 0; + + this->blockConnection(true); + Gui::Selection().clearSelection(); + //we should only be working with 1 entity, but oh well do the loop anyway. + std::vector::const_iterator it; + for (it = selections1.selections.begin(); it != selections1.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::selection2Slot(bool checked) +{ + if (!checked) + return; + buttonSelectedIndex = 1; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections2.selections.begin(); it != selections2.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::resetDialogSlot(bool) +{ + clearSelectionStrings(); + this->blockConnection(true); + Gui::Selection().clearSelection(); + stepped->getButton(0)->setChecked(true); + this->blockConnection(false); +} + +void PartGui::TaskMeasureLinear::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::TaskMeasureLinear::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::TaskMeasureLinear::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +PartGui::VectorAdapter::VectorAdapter() : status(false), vector() +{ +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn) : + status(false), vector(), origin(pickedPointIn) +{ + Handle_Geom_Surface surface = BRep_Tool::Surface(faceIn); + if (surface->IsKind(STANDARD_TYPE(Geom_ElementarySurface))) + { + Handle_Geom_ElementarySurface eSurface = Handle(Geom_ElementarySurface)::DownCast(surface); + gp_Dir direction = eSurface->Axis().Direction(); + vector = direction; + vector.Normalize(); + if (faceIn.Orientation() == TopAbs_REVERSED) + vector.Reverse(); + if (surface->IsKind(STANDARD_TYPE(Geom_CylindricalSurface)) || + surface->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) + ) + { + origin = eSurface->Axis().Location().XYZ(); + projectOriginOntoVector(pickedPointIn); + } + else + origin = pickedPointIn + vector; + status = true; + } +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn) : + status(false), vector(), origin(pickedPointIn) +{ + TopoDS_Vertex firstVertex = TopExp::FirstVertex(edgeIn, Standard_True); + TopoDS_Vertex lastVertex = TopExp::LastVertex(edgeIn, Standard_True); + vector = PartGui::convert(lastVertex) - PartGui::convert(firstVertex); + if (vector.Magnitude() < Precision::Confusion()) + return; + vector.Normalize(); + + status = true; + projectOriginOntoVector(pickedPointIn); +} + +PartGui::VectorAdapter::VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In) : + status(false), vector(), origin() +{ + vector = PartGui::convert(vertex2In) - PartGui::convert(vertex1In); + vector.Normalize(); + + //build origin half way. + gp_Vec tempVector = (PartGui::convert(vertex2In) - PartGui::convert(vertex1In)); + double mag = tempVector.Magnitude(); + tempVector.Normalize(); + tempVector *= (mag / 2.0); + origin = tempVector + PartGui::convert(vertex1In); + + status = true; +} + +PartGui::VectorAdapter::VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2) : + status(false), vector(), origin() +{ + vector = vector2- vector1; + vector.Normalize(); + + //build origin half way. + gp_Vec tempVector = vector2 - vector1; + double mag = tempVector.Magnitude(); + tempVector.Normalize(); + tempVector *= (mag / 2.0); + origin = tempVector + vector1; + + status = true; +} + +void PartGui::VectorAdapter::projectOriginOntoVector(const gp_Vec &pickedPointIn) +{ + Handle_Geom_Curve heapLine = new Geom_Line(origin.XYZ(), vector.XYZ()); + gp_Pnt tempPoint(pickedPointIn.XYZ()); + GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine); + if (projection.NbPoints() < 1) + return; + origin.SetXYZ(projection.Point(1).XYZ()); +} + +PartGui::VectorAdapter::operator gp_Lin() const +{ + gp_Pnt tempOrigin; + tempOrigin.SetXYZ(origin.XYZ()); + return gp_Lin(tempOrigin, gp_Dir(vector)); +} + +gp_Vec PartGui::convert(const TopoDS_Vertex &vertex) +{ + gp_Pnt point = BRep_Tool::Pnt(vertex); + gp_Vec out(point.X(), point.Y(), point.Z()); + return out; +} + +void PartGui::goDimensionAngularRoot() +{ + PartGui::ensure3dDimensionVisible(); + + VectorAdapter adapter1, adapter2; + if(PartGui::evaluateAngularPreSelection(adapter1, adapter2)) + goDimensionAngularNoTask(adapter1, adapter2); + else + { + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (!dlg) + { + Gui::Selection().clearSelection(); + dlg = new PartGui::TaskMeasureAngular(); + } + Gui::Control().showDialog(dlg); + } + Gui::Selection().clearSelection(); +} + +bool PartGui::evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out) +{ + std::vector selections = Gui::Selection().getSelection(); + if (selections.size() > 4 || selections.size() < 2) + return false; + std::vector::iterator it; + std::vector adapters; + TopoDS_Vertex lastVertex; + for (it = selections.begin(); it != selections.end(); ++it) + { + Part::Feature *feature = dynamic_cast((*it).pObject); + if (!feature) + break; + TopoDS_Shape shape = feature->Shape.getValue(); + if (strlen((*it).SubName) > 0) + shape = feature->Shape.getShape().getSubShape((*it).SubName); + if (shape.IsNull()) + break; + + if (shape.ShapeType() == TopAbs_VERTEX) + { + TopoDS_Vertex currentVertex = TopoDS::Vertex(shape); + if (!lastVertex.IsNull()) + { + //need something here for 0 length vector. + //create a point half way between to vertices. + adapters.push_back(VectorAdapter(currentVertex, lastVertex)); + lastVertex = TopoDS_Vertex(); + } + else + { + lastVertex = currentVertex; + } + continue; + } + //vertices have to be selected in succession. so if we make it here clear the last vertex. + lastVertex = TopoDS_Vertex(); + + gp_Vec pickPoint(it->x, it->y, it->z); + //can't use selections without a pick point. + if (pickPoint.IsEqual(gp_Vec(0.0, 0.0, 0.0), Precision::Confusion(), Precision::Angular())) + { + Base::Console().Message("Can't use selections without a pick point.\n"); + continue; + } + + if (shape.ShapeType() == TopAbs_EDGE) + { + TopoDS_Edge edge = TopoDS::Edge(shape); + // make edge orientation so that end of edge closest to pick is head of vector. + gp_Vec firstPoint = PartGui::convert(TopExp::FirstVertex(edge, Standard_True)); + gp_Vec lastPoint = PartGui::convert(TopExp::LastVertex(edge, Standard_True)); + double firstDistance = (firstPoint - pickPoint).Magnitude(); + double lastDistance = (lastPoint - pickPoint).Magnitude(); + if (lastDistance > firstDistance) + { + if (edge.Orientation() == TopAbs_FORWARD) + edge.Orientation(TopAbs_REVERSED); + else + edge.Orientation(TopAbs_FORWARD); + } + adapters.push_back(VectorAdapter(edge, pickPoint)); + continue; + } + + if (shape.ShapeType() == TopAbs_FACE) + { + TopoDS_Face face = TopoDS::Face(shape); + adapters.push_back(VectorAdapter(face, pickPoint)); + continue; + } + } + + if (adapters.size() != 2) + return false; + if (!adapters.front().isValid() || !adapters.back().isValid()) + return false; + + vector1Out = adapters.front(); + vector2Out = adapters.back(); + + //making sure pick points are not equal + if ((vector1Out.getPickPoint() - vector2Out.getPickPoint()).Magnitude() < std::numeric_limits::epsilon()) + { + Base::Console().Message("pick points are equal\n"); + return false; + } + + return true; +} + +void PartGui::goDimensionAngularNoTask(const VectorAdapter &vector1Adapter, const VectorAdapter &vector2Adapter) +{ + gp_Vec vector1 = vector1Adapter; + gp_Vec vector2 = vector2Adapter; + double angle = vector1.Angle(vector2); + + std::ostringstream stream; + stream << std::setprecision(std::numeric_limits::digits10 + 1) << std::fixed << std::endl << + "angle in radians is: " << angle << std::endl << + "angle in degrees is: " << 180 * angle / M_PI << std::endl; + if (angle < M_PI / 2.0) + stream << std::setprecision(std::numeric_limits::digits10 + 1) << + "complement in radians is: " << M_PI / 2.0 - angle << std::endl << + "complement in degrees is: " << 90 - 180 * angle / M_PI << std::endl; + //I don't think we get anything over 180, but just in case. + if (angle > M_PI / 2.0 && angle < M_PI) + stream << std::setprecision(std::numeric_limits::digits10 + 1) << + "supplement in radians is: " << M_PI - angle << std::endl << + "supplement in degrees is: " << 180 - 180 * angle / M_PI << std::endl; + Base::Console().Message(stream.str().c_str()); + + SbMatrix dimSys; + double radius; + double displayAngle;//have to fake the angle in the 3d. + + if (vector1.IsParallel(vector2, Precision::Angular())) + { + //take first point project it onto second vector. + Handle_Geom_Curve heapLine2 = new Geom_Line(vector2Adapter); + gp_Pnt tempPoint(vector1Adapter.getPickPoint().XYZ()); + + GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine2); + if (projection.NbPoints() < 1) + { + Base::Console().Message("parallel vectors: couldn't project onto line\n"); + return; + } + gp_Vec newPoint2; + newPoint2.SetXYZ(projection.Point(1).XYZ()); + + //if points are colinear, projection doesn't work and returns the same point. + //In this case we just use the original point. + if ((newPoint2 - vector1Adapter.getPickPoint()).Magnitude() < Precision::Confusion()) + newPoint2 = vector2Adapter.getPickPoint(); + + //now get midpoint between for dim origin. + gp_Vec point1 = vector1Adapter.getPickPoint(); + gp_Vec midPointProjection = newPoint2 - point1; + double distance = midPointProjection.Magnitude(); + midPointProjection.Normalize(); + midPointProjection *= distance / 2.0; + + gp_Vec origin = point1 + midPointProjection; + + //yaxis should be the same as vector1, but doing this to eliminate any potential slop from + //using precision::angular. If lines are colinear and we have no plane, we can't establish zAxis from crossing. + //we just the absolute axis. + gp_Vec xAxis = (point1 - origin).Normalized(); + gp_Vec zAxis; + if (xAxis.IsParallel(vector1, Precision::Angular())) + { + if (!xAxis.IsParallel(gp_Vec(0.0, 0.0, 1.0), Precision::Angular())) + zAxis = gp_Vec(0.0, 0.0, 1.0); + else + zAxis = gp_Vec(0.0, 1.0, 0.0); + } + else + zAxis = xAxis.Crossed(vector1).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + zAxis = xAxis.Crossed(yAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), origin.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), origin.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), origin.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + dimSys = dimSys.transpose(); + + radius = midPointProjection.Magnitude(); + displayAngle = M_PI; + } + else + { + Handle_Geom_Curve heapLine1 = new Geom_Line(vector1Adapter); + Handle_Geom_Curve heapLine2 = new Geom_Line(vector2Adapter); + + GeomAPI_ExtremaCurveCurve extrema(heapLine1, heapLine2); + + if (extrema.NbExtrema() < 1) + { + Base::Console().Message("couldn't get extrema\n"); + return; + } + + gp_Pnt extremaPoint1, extremaPoint2, dimensionOriginPoint; + extrema.Points(1, extremaPoint1, extremaPoint2); + if (extremaPoint1.Distance(extremaPoint2) < Precision::Confusion()) + dimensionOriginPoint = extremaPoint1; + else + { + //find half way point in between extrema points for dimension origin. + gp_Vec vec1(extremaPoint1.XYZ()); + gp_Vec vec2(extremaPoint2.XYZ()); + gp_Vec connection(vec2-vec1); + Standard_Real distance = connection.Magnitude(); + connection.Normalize(); + connection *= (distance / 2.0); + dimensionOriginPoint.SetXYZ((vec1 + connection).XYZ()); + } + + gp_Vec thirdPoint(vector2Adapter.getPickPoint()); + gp_Vec originVector(dimensionOriginPoint.XYZ()); + gp_Vec extrema2Vector(extremaPoint2.XYZ()); + radius = (vector1Adapter.getPickPoint() - originVector).Magnitude(); + double legOne = (extrema2Vector - originVector).Magnitude(); + displayAngle = angle; + if (legOne > Precision::Confusion()) + { + double legTwo = sqrt(pow(radius, 2) - pow(legOne, 2)); + gp_Vec projectionVector(vector2); + projectionVector.Normalize(); + projectionVector *= legTwo; + thirdPoint = extrema2Vector + projectionVector; + gp_Vec hyp(thirdPoint - originVector); + hyp.Normalize(); + gp_Vec otherSide(vector1Adapter.getPickPoint() - originVector); + otherSide.Normalize(); + displayAngle = hyp.Angle(otherSide); + } + + gp_Vec xAxis = (vector1Adapter.getPickPoint() - originVector).Normalized(); + gp_Vec fakeYAxis = (thirdPoint - originVector).Normalized(); + gp_Vec zAxis = (xAxis.Crossed(fakeYAxis)).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), dimensionOriginPoint.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), dimensionOriginPoint.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), dimensionOriginPoint.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + + dimSys = dimSys.transpose(); + } + + DimensionAngular *dimension = new DimensionAngular(); + dimension->matrix.setValue(dimSys); + dimension->radius.setValue(radius); + dimension->angle.setValue(static_cast(displayAngle)); + dimension->text.setValue((Base::Quantity(180 * angle / M_PI, Base::Unit::Angle)).getUserString().toUtf8().constData()); + dimension->dColor.setValue(SbColor(0.0, 0.0, 1.0)); + + Gui::View3DInventorViewer *viewer = getViewer(); + if (!viewer) + return; + + viewer->addDimension3d(dimension); +} + +SO_KIT_SOURCE(PartGui::DimensionAngular); + +void PartGui::DimensionAngular::initClass() +{ + SO_KIT_INIT_CLASS(DimensionAngular, SoSeparatorKit, "SeparatorKit"); +} + +PartGui::DimensionAngular::DimensionAngular() +{ + SO_KIT_CONSTRUCTOR(PartGui::DimensionAngular); + + SO_KIT_ADD_CATALOG_ENTRY(transformation, SoMatrixTransform, TRUE, topSeparator,"" , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(annotate, SoAnnotation, TRUE, topSeparator,"" , TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arrow1, SoShapeKit, TRUE, topSeparator,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arrow2, SoShapeKit, TRUE, topSeparator,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(arcSep, SoSeparator, TRUE, annotate,"" ,TRUE); + SO_KIT_ADD_CATALOG_ENTRY(textSep, SoSeparator, TRUE, annotate,"" ,TRUE); + + SO_KIT_INIT_INSTANCE(); + + SO_NODE_ADD_FIELD(radius, (10.0)); + SO_NODE_ADD_FIELD(angle, (1.0)); + SO_NODE_ADD_FIELD(text, ("test"));//dimension text + SO_NODE_ADD_FIELD(dColor, (1.0, 0.0, 0.0));//dimension color. + SO_NODE_ADD_FIELD(matrix, (1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0)); + + setupDimension(); +} + +PartGui::DimensionAngular::~DimensionAngular() +{ + +} + +SbBool PartGui::DimensionAngular::affectsState() const +{ + return FALSE; +} + + +void PartGui::DimensionAngular::setupDimension() +{ + //transformation + SoMatrixTransform *trans = static_cast(getPart("transformation", TRUE)); + trans->matrix.connectFrom(&matrix); + + //color + SoMaterial *material = new SoMaterial; + material->diffuseColor.connectFrom(&dColor); + + //dimension arrows. + SoCone *cone = new SoCone(); + cone->bottomRadius.setValue(0.25); + cone->height.setValue(0.5); + + setPart("arrow1.shape", cone); + set("arrow1.localTransform", "rotation 0.0 0.0 1.0 3.1415927"); + set("arrow1.localTransform", "translation 0.0 0.25 0.0"); //half cone height. + setPart("arrow2.shape", cone); + set("arrow2.transform", "rotation 0.0 0.0 1.0 0.0"); + set("arrow2.localTransform", "translation 0.0 -0.25 0.0"); //half cone height. + + //I was getting errors if I didn't manually allocate for these transforms. Not sure why. + SoTransform *arrow1Transform = new SoTransform(); + SoComposeVec3f *arrow1Compose = new SoComposeVec3f(); + arrow1Compose->x.connectFrom(&radius); + arrow1Compose->y.setValue(0.0); + arrow1Compose->y.setValue(0.0); + arrow1Transform->translation.connectFrom(&arrow1Compose->vector); + setPart("arrow1.transform", arrow1Transform); + + SoComposeRotation *arrow2Rotation = new SoComposeRotation(); + arrow2Rotation->angle.connectFrom(&angle); + arrow2Rotation->axis.setValue(0.0, 0.0, 1.0); + SoTransform *arrow2Transform = new SoTransform(); + arrow2Transform->rotation.connectFrom(&arrow2Rotation->rotation); + SoCalculator *arrow2LocationCalc = new SoCalculator(); + arrow2LocationCalc->a.connectFrom(&angle); + arrow2LocationCalc->b.connectFrom(&radius); + arrow2LocationCalc->expression.set1Value(0, "oa = cos(a) * b"); //xLocation + arrow2LocationCalc->expression.set1Value(1, "ob = sin(a) * b"); //yLocation + SoComposeVec3f *arrow2Compose = new SoComposeVec3f(); + arrow2Compose->x.connectFrom(&arrow2LocationCalc->oa); + arrow2Compose->y.connectFrom(&arrow2LocationCalc->ob); + arrow2Compose->z.setValue(0.0f); + arrow2Transform->translation.connectFrom(&arrow2Compose->vector); + setPart("arrow2.transform", arrow2Transform); + + setPart("arrow1.material", material); + setPart("arrow2.material", material); + + ArcEngine *arcEngine = new ArcEngine(); + arcEngine->angle.connectFrom(&angle); + arcEngine->radius.connectFrom(&radius); + arcEngine->deviation.setValue(0.1f); + + SoCoordinate3 *coordinates = new SoCoordinate3(); + coordinates->point.connectFrom(&arcEngine->points); + + SoLineSet *lineSet = new SoLineSet(); + lineSet->vertexProperty.setValue(coordinates); + lineSet->numVertices.connectFrom(&arcEngine->pointCount); + lineSet->startIndex.setValue(0); + + SoSeparator *arcSep = static_cast(getPart("arcSep", TRUE)); + if (!arcSep) + return; + arcSep->addChild(material); + arcSep->addChild(lineSet); + + //text + SoSeparator *textSep = static_cast(getPart("textSep", TRUE)); + if (!textSep) + return; + + textSep->addChild(material); + + SoCalculator *textVecCalc = new SoCalculator(); + textVecCalc->a.connectFrom(&angle); + textVecCalc->b.connectFrom(&radius); + textVecCalc->expression.set1Value(0, "oa = a / 2.0"); + textVecCalc->expression.set1Value(1, "ob = cos(oa) * b"); //x + textVecCalc->expression.set1Value(2, "oc = sin(oa) * b"); //y + + SoComposeVec3f *textLocation = new SoComposeVec3f(); + textLocation->x.connectFrom(&textVecCalc->ob); + textLocation->y.connectFrom(&textVecCalc->oc); + textLocation->z.setValue(0.0); + + + SoTransform *textTransform = new SoTransform(); + textTransform->translation.connectFrom(&textLocation->vector); + textSep->addChild(textTransform); + + SoFont *fontNode = new SoFont(); + fontNode->name.setValue("defaultFont"); + fontNode->size.setValue(30); + textSep->addChild(fontNode); + + SoText2 *textNode = new SoText2(); + textNode->justification = SoText2::CENTER; + textNode->string.connectFrom(&text); + textSep->addChild(textNode); + + //this prevents the 2d text from screwing up the bounding box for a viewall + SoResetTransform *rTrans = new SoResetTransform; + rTrans->whatToReset = SoResetTransform::BBOX; + textSep->addChild(rTrans); +} + +SO_ENGINE_SOURCE(PartGui::ArcEngine); + +PartGui::ArcEngine::ArcEngine() +{ + SO_ENGINE_CONSTRUCTOR(ArcEngine); + + SO_ENGINE_ADD_INPUT(radius, (10.0)); + SO_ENGINE_ADD_INPUT(angle, (1.0)); + SO_ENGINE_ADD_INPUT(deviation, (0.25)); + + SO_ENGINE_ADD_OUTPUT(points, SoMFVec3f); + SO_ENGINE_ADD_OUTPUT(pointCount, SoSFInt32); +} + +void PartGui::ArcEngine::initClass() +{ + SO_ENGINE_INIT_CLASS(ArcEngine, SoEngine, "Engine"); +} + +void PartGui::ArcEngine::evaluate() +{ + if (radius.getValue() < std::numeric_limits::epsilon() || + angle.getValue() < std::numeric_limits::epsilon() || + deviation.getValue() < std::numeric_limits::epsilon()) + { + defaultValues(); + return; + } + + float deviationAngle(acos((radius.getValue() - deviation.getValue()) / radius.getValue())); + std::vector tempPoints; + int segmentCount; + if (deviationAngle >= angle.getValue()) + segmentCount = 1; + else + { + segmentCount = static_cast(angle.getValue() / deviationAngle) + 1; + if (segmentCount < 2) + { + defaultValues(); + return; + } + } + float angleIncrement = angle.getValue() / static_cast(segmentCount); + for (int index = 0; index < segmentCount + 1; ++index) + { + SbVec3f currentNormal(1.0, 0.0, 0.0); + float currentAngle = index * angleIncrement; + SbRotation rotation(SbVec3f(0.0, 0.0, 1.0), currentAngle); + rotation.multVec(currentNormal, currentNormal); + tempPoints.push_back(currentNormal * radius.getValue()); + } + int tempCount = tempPoints.size(); //for macro. + SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(tempCount)); + SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(tempCount)); + std::vector::const_iterator it; + for (it = tempPoints.begin(); it != tempPoints.end(); ++it) + { + int currentIndex = it-tempPoints.begin(); //for macro. + SbVec3f temp(*it); //for macro + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(currentIndex, temp)); + } + +} + +void PartGui::ArcEngine::defaultValues() +{ + //just some non-failing info. + SO_ENGINE_OUTPUT(points, SoMFVec3f, setNum(2)); + SbVec3f point1(10.0, 0.0, 0.0); + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(0, point1)); + SbVec3f point2(7.07, 7.07, 0.0); + SO_ENGINE_OUTPUT(points, SoMFVec3f, set1Value(1, point2)); + SO_ENGINE_OUTPUT(pointCount, SoSFInt32, setValue(2)); +} + +PartGui::SteppedSelection::SteppedSelection(const uint& buttonCountIn, QWidget* parent): + QWidget(parent) +{ + if (buttonCountIn < 1) + return; + + QVBoxLayout *mainLayout = new QVBoxLayout(); + this->setLayout(mainLayout); + + QButtonGroup *buttonGroup = new QButtonGroup(); + buttonGroup->setExclusive(true); + + for (uint index = 0; index < buttonCountIn; ++index) + { + ButtonIconPairType tempPair; + + std::ostringstream stream; + stream << "Selection " << ((index < 10) ? "0" : "") << index + 1; + QString buttonText = QObject::tr(stream.str().c_str()); + QPushButton *button = new QPushButton(buttonText, this); + button->setCheckable(true); + buttonGroup->addButton(button); + connect(button, SIGNAL(toggled(bool)), this, SLOT(selectionSlot(bool))); + + QLabel *label = new QLabel; + + tempPair.first = button; + tempPair.second = label; + buttons.push_back(tempPair); + + QHBoxLayout *layout = new QHBoxLayout(); + mainLayout->addLayout(layout); + layout->addWidget(button); + layout->addSpacing(10); + layout->addWidget(label); + layout->addStretch(); + } + mainLayout->addStretch(); + + buildPixmaps(); //uses button size +} + +PartGui::SteppedSelection::~SteppedSelection() +{ + if(stepActive) + { + delete stepActive; + stepActive = 0; + } + if (stepDone) + { + delete stepDone; + stepDone = 0; + } +} + +void PartGui::SteppedSelection::buildPixmaps() +{ + assert(buttons.size() > 0); + int iconHeight(buttons.at(0).first->height()-6); + stepActive = new QPixmap(Gui::BitmapFactory().pixmap("Part_Measure_Step_Active").scaled + (iconHeight, iconHeight, Qt::KeepAspectRatio)); + stepDone = new QPixmap(Gui::BitmapFactory().pixmap("Part_Measure_Step_Done").scaled + (iconHeight, iconHeight, Qt::KeepAspectRatio)); +} + +void PartGui::SteppedSelection::selectionSlot(bool checked) +{ + QPushButton *sender = qobject_cast(QObject::sender()); + assert(sender != 0); + std::vector::iterator it; + for (it = buttons.begin(); it != buttons.end(); ++it) + if (it->first == sender) + break; + assert(it != buttons.end()); + + if (checked) + it->second->setPixmap(*stepActive); + else + it->second->setPixmap(QPixmap()); +} + +QPushButton* PartGui::SteppedSelection::getButton(const uint& index) +{ + return buttons.at(index).first; +} + +void PartGui::SteppedSelection::setIconDone(const uint& index) +{ + buttons.at(index).second->setPixmap(*stepDone); +} + +PartGui::DimensionControl::DimensionControl(QWidget* parent): QWidget(parent) +{ + QVBoxLayout *commandLayout = new QVBoxLayout(); + this->setLayout(commandLayout); + + QPushButton *toggle3dButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Toggle_3d"), + QObject::tr("Toggle 3d"), this); + QObject::connect(toggle3dButton, SIGNAL(clicked(bool)), this, SLOT(toggle3dSlot(bool))); + QHBoxLayout *layout = new QHBoxLayout(); + layout->addWidget(toggle3dButton); + layout->addStretch(); + commandLayout->addLayout(layout); + + QPushButton *toggleDeltaButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Toggle_Delta"), + QObject::tr("Toggle Delta"), this); + QObject::connect(toggleDeltaButton, SIGNAL(clicked(bool)), this, SLOT(toggleDeltaSlot(bool))); + layout = new QHBoxLayout(); + layout->addWidget(toggleDeltaButton); + layout->addStretch(); + commandLayout->addLayout(layout); + + QPushButton *clearAllButton = new QPushButton(Gui::BitmapFactory().pixmap("Part_Measure_Clear_All"), + QObject::tr("Clear All"), this); + QObject::connect(clearAllButton, SIGNAL(clicked(bool)), this, SLOT(clearAllSlot(bool))); + layout = new QHBoxLayout(); + layout->addWidget(clearAllButton); + layout->addStretch(); + commandLayout->addLayout(layout); +} + +void PartGui::DimensionControl::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::DimensionControl::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::DimensionControl::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +PartGui::TaskMeasureAngular::TaskMeasureAngular(): selections1(), selections2(), buttonSelectedIndex(0) +{ + setUpGui(); +} + +PartGui::TaskMeasureAngular::~TaskMeasureAngular() +{ + Gui::Selection().clearSelection(); +} + +void PartGui::TaskMeasureAngular::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + TopoDS_Shape shape; + if (!getShapeFromStrings(shape, std::string(msg.pDocName), std::string(msg.pObjectName), std::string(msg.pSubName))) + return; + DimSelections::DimSelection newSelection; + newSelection.documentName = msg.pDocName; + newSelection.objectName = msg.pObjectName; + newSelection.subObjectName = msg.pSubName; + newSelection.x = msg.x; + newSelection.y = msg.y; + newSelection.z = msg.z; + gp_Vec pickPoint(msg.x, msg.y, msg.z); + if (buttonSelectedIndex == 0) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + if (shape.ShapeType() == TopAbs_VERTEX) + { + //if we have previous selection it should be only one vertex. + if (selections1.selections.size() > 1) + selections1.selections.clear(); + else if(selections1.selections.size() == 1) + { + //make sure it is a vertex. + if (selections1.selections.at(0).shapeType != DimSelections::Vertex) + selections1.selections.clear(); + } + + newSelection.shapeType = DimSelections::Vertex; + selections1.selections.push_back(newSelection); + if (selections1.selections.size() == 1) + return; + //here we should have 2 vertices, but will check to make sure. + assert(selections1.selections.size() == 2); + assert(selections1.selections.at(0).shapeType == DimSelections::Vertex); + assert(selections1.selections.at(1).shapeType == DimSelections::Vertex); + + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + + //here there should only be one in the selections container. so just clear it. + selections1.selections.clear(); + + if (shape.ShapeType() == TopAbs_EDGE) + { + newSelection.shapeType = DimSelections::Edge; + selections1.selections. push_back(newSelection); + } + + if (shape.ShapeType() == TopAbs_FACE) + { + newSelection.shapeType = DimSelections::Face; + selections1.selections.push_back(newSelection); + } + + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(1)->setChecked(true); + return; + } + } + if (buttonSelectedIndex == 1) + { + if (msg.Type == Gui::SelectionChanges::AddSelection) + { + if (shape.ShapeType() == TopAbs_VERTEX) + { + //if we have previous selection it should be only one vertex. + if (selections2.selections.size() > 1) + selections2.selections.clear(); + else if(selections2.selections.size() == 1) + { + //make sure it is a vertex. + if (selections2.selections.at(0).shapeType != DimSelections::Vertex) + selections2.selections.clear(); + } + + newSelection.shapeType = DimSelections::Vertex; + selections2.selections.push_back(newSelection); + if (selections2.selections.size() == 1) + return; + //here we should have 2 vertices, but will check to make sure. + assert(selections2.selections.size() == 2); + assert(selections2.selections.at(0).shapeType == DimSelections::Vertex); + assert(selections2.selections.at(1).shapeType == DimSelections::Vertex); + + buildDimension(); + clearSelection(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + //vertices have to be selected in succession. if we get here,clear temp selection. + selections2.selections.clear(); + + if (shape.ShapeType() == TopAbs_EDGE) + { + newSelection.shapeType = DimSelections::Edge; + selections2.selections. push_back(newSelection); + } + + if (shape.ShapeType() == TopAbs_FACE) + { + newSelection.shapeType = DimSelections::Face; + selections2.selections.push_back(newSelection); + } + + buildDimension(); + clearSelection(); + QTimer::singleShot(0, this, SLOT(selectionClearDelayedSlot())); + stepped->getButton(0)->setChecked(true); + return; + } + } +} + +void PartGui::TaskMeasureAngular::selectionClearDelayedSlot() +{ + //hack. + //clearing selections are not working as I hoped. Apparently the observer callback gets called + //before the actual selection takes place. Resulting in selections being left. this addresses this + //by being called from the event loop. + this->blockConnection(true); + Gui::Selection().clearSelection(); + this->blockConnection(false); +} + +PartGui::VectorAdapter PartGui::TaskMeasureAngular::buildAdapter(const PartGui::DimSelections& selection) const +{ + assert(selection.selections.size() > 0 && selection.selections.size() < 3); + if (selection.selections.size() == 1) + { + DimSelections::DimSelection current = selection.selections.at(0); + if (current.shapeType == DimSelections::Edge) + { + TopoDS_Shape edgeShape; + if (!getShapeFromStrings(edgeShape, current.documentName, current.objectName, current.subObjectName)) + return VectorAdapter(); + TopoDS_Edge edge = TopoDS::Edge(edgeShape); + // make edge orientation so that end of edge closest to pick is head of vector. + gp_Vec firstPoint = PartGui::convert(TopExp::FirstVertex(edge, Standard_True)); + gp_Vec lastPoint = PartGui::convert(TopExp::LastVertex(edge, Standard_True)); + gp_Vec pickPoint(current.x, current.y, current.z); + double firstDistance = (firstPoint - pickPoint).Magnitude(); + double lastDistance = (lastPoint - pickPoint).Magnitude(); + if (lastDistance > firstDistance) + { + if (edge.Orientation() == TopAbs_FORWARD) + edge.Orientation(TopAbs_REVERSED); + else + edge.Orientation(TopAbs_FORWARD); + } + return VectorAdapter(edge, pickPoint); + } + if (current.shapeType == DimSelections::Face) + { + TopoDS_Shape faceShape; + if (!getShapeFromStrings(faceShape, current.documentName, current.objectName, current.subObjectName)) + return VectorAdapter(); + + TopoDS_Face face = TopoDS::Face(faceShape); + gp_Vec pickPoint(current.x, current.y, current.z); + return VectorAdapter(face, pickPoint); + } + } + //selection size == 2. + DimSelections::DimSelection current1 = selection.selections.at(0); + DimSelections::DimSelection current2 = selection.selections.at(1); + assert(current1.shapeType == DimSelections::Vertex); + assert(current2.shapeType == DimSelections::Vertex); + TopoDS_Shape vertexShape1, vertexShape2; + if(!getShapeFromStrings(vertexShape1, current1.documentName, current1.objectName, current1.subObjectName)) + return VectorAdapter(); + if(!getShapeFromStrings(vertexShape2, current2.documentName, current2.objectName, current2.subObjectName)) + return VectorAdapter(); + + TopoDS_Vertex vertex1 = TopoDS::Vertex(vertexShape1); + TopoDS_Vertex vertex2 = TopoDS::Vertex(vertexShape2); + + //build a temp adapter to make sure it is valid. + return VectorAdapter(PartGui::convert(vertex2), PartGui::convert(vertex1)); +} + +void PartGui::TaskMeasureAngular::buildDimension() +{ + //build adapters. + VectorAdapter adapt1 = buildAdapter(selections1); + VectorAdapter adapt2 = buildAdapter(selections2); + + if (!adapt1.isValid() || !adapt2.isValid()) + { + Base::Console().Message("\ncouldn't build adapter\n\n"); + return; + } + goDimensionAngularNoTask(adapt1, adapt2); +} + +void PartGui::TaskMeasureAngular::clearSelection() +{ + selections1.selections.clear(); + selections2.selections.clear(); +} + +void PartGui::TaskMeasureAngular::setUpGui() +{ + QPixmap mainIcon = Gui::BitmapFactory().pixmap("Part_Measure_Angular"); + + Gui::TaskView::TaskBox* selectionTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Selections"), false, 0); + QVBoxLayout *selectionLayout = new QVBoxLayout(); + stepped = new SteppedSelection(2, selectionTaskBox); + selectionLayout->addWidget(stepped); + selectionTaskBox->groupLayout()->addLayout(selectionLayout); + + Gui::TaskView::TaskBox* controlTaskBox = new Gui::TaskView::TaskBox + (mainIcon, QObject::tr("Control"), false, 0); + QVBoxLayout *controlLayout = new QVBoxLayout(); + + QHBoxLayout *resetLayout = new QHBoxLayout(); + resetLayout->addSpacing(5); //hack for alignment. + QPushButton *resetButton = new QPushButton(mainIcon, QObject::tr("Reset Dialog"), controlTaskBox); + resetLayout->addWidget(resetButton); + resetLayout->addStretch(); + controlLayout->addLayout(resetLayout); + QObject::connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetDialogSlot(bool))); + + DimensionControl *control = new DimensionControl(controlTaskBox); + controlLayout->addWidget(control); + controlTaskBox->groupLayout()->addLayout(controlLayout); + + this->setButtonPosition(TaskDialog::South); + Content.push_back(selectionTaskBox); + Content.push_back(controlTaskBox); + + stepped->getButton(0)->setChecked(true);//before wired up. + QObject::connect(stepped->getButton(0), SIGNAL(toggled(bool)), this, SLOT(selection1Slot(bool))); + QObject::connect(stepped->getButton(1), SIGNAL(toggled(bool)), this, SLOT(selection2Slot(bool))); +} + +void PartGui::TaskMeasureAngular::selection1Slot(bool checked) +{ + if (checked) + { + buttonSelectedIndex = 0; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections1.selections.begin(); it != selections1.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); + } + else + { + if (selections1.selections.size() > 0) + stepped->setIconDone(0); + } +} + +void PartGui::TaskMeasureAngular::selection2Slot(bool checked) +{ + if (checked) + buttonSelectedIndex = 1; + this->blockConnection(true); + Gui::Selection().clearSelection(); + std::vector::const_iterator it; + for (it = selections2.selections.begin(); it != selections2.selections.end(); ++it) + Gui::Selection().addSelection(it->documentName.c_str(), it->objectName.c_str(), it->subObjectName.c_str()); + this->blockConnection(false); +} + +void PartGui::TaskMeasureAngular::resetDialogSlot(bool) +{ + clearSelection(); + this->blockConnection(true); + Gui::Selection().clearSelection(); + stepped->getButton(0)->setChecked(true); + this->blockConnection(false); +} + +void PartGui::TaskMeasureAngular::toggle3dSlot(bool) +{ + PartGui::toggle3d(); +} + +void PartGui::TaskMeasureAngular::toggleDeltaSlot(bool) +{ + PartGui::toggleDelta(); +} + +void PartGui::TaskMeasureAngular::clearAllSlot(bool) +{ + PartGui::eraseAllDimensions(); +} + +#include "moc_TaskDimension.cpp" diff --git a/src/Mod/Part/Gui/TaskDimension.h b/src/Mod/Part/Gui/TaskDimension.h new file mode 100644 index 000000000..de09b90c4 --- /dev/null +++ b/src/Mod/Part/Gui/TaskDimension.h @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (c) 2013 Thomas Anderson * + * * + * 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 * + * * + ***************************************************************************/ + +#ifndef TASKDIMENSION_H +#define TASKDIMENSION_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class TopoDS_Shape; +class TopoDS_Face; +class TopoDS_Edge; +class TopoDS_Vertex; +class gp_Pnt; +class BRepExtrema_DistShapeShape; + +class QPushButton; +class QPixmap; +class QLabel; + +namespace Gui{class View3dInventorViewer;} + +namespace PartGui +{ + /*!find shape from selection strings + * @param shapeOut search results. + * @param doc document name to search. + * @param object object name to search. + * @param sub sub-object name to search. + * @return signal if the search was successful. + */ + bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub); + /*!examine pre selection + * @param shape1 firt shape in current selection + * @param shape2 second shape in current selection + * @return signal if preselection is valid. false means shape1 and shape2 are invalid. + */ + bool evaluateLinearPreSelection(TopoDS_Shape &shape1, TopoDS_Shape &shape2); + /*!start of the measure linear command*/ + void goDimensionLinearRoot(); + /*!does the measure and create dimensions without a dialog + * @param shape1 first shape. + * @param shape2 second shape. + * @todo incorporate some form of "adapt to topods_shape". so we can expand to other types outside OCC. + */ + void goDimensionLinearNoTask(const TopoDS_Shape &shape1, const TopoDS_Shape &shape2); + /*!prints results of measuring to console. + * @param measure object containing the measure information + */ + void dumpLinearResults(const BRepExtrema_DistShapeShape &measure); + /*!convenience function to get the viewer*/ + Gui::View3DInventorViewer* getViewer(); + /*!adds 3d and delta dimensions to the viewer + * @param measure object containing the measure information. + */ + void addLinearDimensions(const BRepExtrema_DistShapeShape &measure); + /*!creates one dimension from points with color + * @param point1 first point + * @param point2 second point + * @param color color of dimension + * @return an inventor node to add to a scenegraph + */ + SoNode* createLinearDimension(const gp_Pnt &point1, const gp_Pnt &point2, const SbColor &color); + /*!erases all the dimensions in the viewer.*/ + void eraseAllDimensions(); + /*!toggles the display status of the 3d dimensions*/ + void toggle3d(); + /*!toggles the display status of the delta dimensions*/ + void toggleDelta(); + /*!make sure measure command isn't working with everthing invisible. Confusing the user*/ + void ensureSomeDimensionVisible(); + /*!make sure angle measure command isn't working with 3d off. Confusing the user*/ + void ensure3dDimensionVisible(); + /*convert a vertex to vector*/ + gp_Vec convert(const TopoDS_Vertex &vertex); + +class DimensionLinear : public SoSeparatorKit +{ + SO_KIT_HEADER(DimensionLinear); + + SO_KIT_CATALOG_ENTRY_HEADER(transformation); + SO_KIT_CATALOG_ENTRY_HEADER(annotate); + SO_KIT_CATALOG_ENTRY_HEADER(leftArrow); + SO_KIT_CATALOG_ENTRY_HEADER(rightArrow); + SO_KIT_CATALOG_ENTRY_HEADER(line); + SO_KIT_CATALOG_ENTRY_HEADER(textSep); +public: + DimensionLinear(); + static void initClass(); + virtual SbBool affectsState() const; + + SoSFVec3f point1; + SoSFVec3f point2; + SoSFString text; + SoSFColor dColor; +protected: + SoSFRotation rotate; + SoSFFloat length; + SoSFVec3f origin; + +private: + virtual ~DimensionLinear(); + void setupDimension(); +}; + +/*kit for anglular dimensions*/ +class DimensionAngular : public SoSeparatorKit +{ + SO_KIT_HEADER(DimensionAngular); + + SO_KIT_CATALOG_ENTRY_HEADER(transformation); + SO_KIT_CATALOG_ENTRY_HEADER(annotate); + SO_KIT_CATALOG_ENTRY_HEADER(arrow1); + SO_KIT_CATALOG_ENTRY_HEADER(arrow2); + SO_KIT_CATALOG_ENTRY_HEADER(arcSep); + SO_KIT_CATALOG_ENTRY_HEADER(textSep); +public: + DimensionAngular(); + static void initClass(); + virtual SbBool affectsState() const; + + SoSFFloat radius;//radians. + SoSFFloat angle;//radians. + SoSFString text; + SoSFColor dColor; + SoSFMatrix matrix; +private: + virtual ~DimensionAngular(); + void setupDimension(); +}; + +/*used for generating points for arc display*/ +class ArcEngine : public SoEngine +{ + SO_ENGINE_HEADER(ArcEngine); +public: + ArcEngine(); + static void initClass(); + + SoSFFloat radius; + SoSFFloat angle; + SoSFFloat deviation; + + SoEngineOutput points; + SoEngineOutput pointCount; +protected: + virtual void evaluate(); +private: + virtual ~ArcEngine(){} + void defaultValues(); //some non error values if something goes wrong. +}; + +/*! a widget with buttons and icons for a controlled selection process*/ +class SteppedSelection : public QWidget +{ + Q_OBJECT +public: + SteppedSelection(const uint &buttonCountIn, QWidget *parent = 0); + ~SteppedSelection(); + QPushButton* getButton(const uint &index); + void setIconDone(const uint &index); + +protected: + typedef std::pair ButtonIconPairType; + std::vector buttons; + QPixmap *stepActive; + QPixmap *stepDone; + +private slots: + void selectionSlot(bool checked); + void buildPixmaps(); + +}; + +/*! just convenience container*/ +class DimSelections +{ +public: + enum ShapeType{None, Vertex, Edge, Face}; + struct DimSelection + { + std::string documentName; + std::string objectName; + std::string subObjectName; + float x; + float y; + float z; + ShapeType shapeType; + }; + std::vector selections; +}; + +/*!widget for buttons controlling the display of dimensions*/ +class DimensionControl : public QWidget +{ + Q_OBJECT +public: + explicit DimensionControl(QWidget* parent); +public slots: + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); +}; + +/*!linear dialog*/ +class TaskMeasureLinear : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver +{ + Q_OBJECT +public: + TaskMeasureLinear(); + ~TaskMeasureLinear(); + + virtual QDialogButtonBox::StandardButtons getStandardButtons() const + {return QDialogButtonBox::Close;} + virtual bool isAllowedAlterDocument(void) const {return false;} + virtual bool needsFullSpace() const {return false;} +protected: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +protected slots: + void selection1Slot(bool checked); + void selection2Slot(bool checked); + void resetDialogSlot(bool); + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); + void selectionClearDelayedSlot(); + +private: + void setUpGui(); + void buildDimension(); + void clearSelectionStrings(); + DimSelections selections1; + DimSelections selections2; + uint buttonSelectedIndex; + SteppedSelection *stepped; + +}; + +/*! @brief Convert to vector + * + * Used to construct a vector from various input types + */ +class VectorAdapter +{ +public: + /*!default construction isValid is set to false*/ + VectorAdapter(); + /*!Build a vector from a faceIn + * @param faceIn vector will be normal to plane and equal to cylindrical axis. + * @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/ + VectorAdapter(const TopoDS_Face &faceIn, const gp_Vec &pickedPointIn); + /*!Build a vector from an edgeIn + * @param edgeIn vector will be lastPoint - firstPoint. + * @param pickedPointIn location of pick. straight conversion from sbvec. not accurate.*/ + VectorAdapter(const TopoDS_Edge &edgeIn, const gp_Vec &pickedPointIn); + /*!Build a vector From 2 vertices. + *vector will be equal to @param vertex2In - @param vertex1In.*/ + VectorAdapter(const TopoDS_Vertex &vertex1In, const TopoDS_Vertex &vertex2In); + /*!Build a vector From 2 vectors. + *vector will be equal to @param vector2 - @param vector1.*/ + VectorAdapter(const gp_Vec &vector1, const gp_Vec &vector2); + + /*!make sure no errors in vector construction. + * @return true = vector is good. false = vector is NOT good.*/ + bool isValid() const {return status;} + /*!get the calculated vector. + * @return the vector. use isValid to ensure correct results.*/ + operator gp_Vec() const {return vector;} + /*!build occ line used for extrema calculation*/ + operator gp_Lin() const; + gp_Vec getPickPoint() const {return origin;} + +private: + void projectOriginOntoVector(const gp_Vec &pickedPointIn); + bool status; + gp_Vec vector; + gp_Vec origin; +}; + +/*!angular dialog class*/ +class TaskMeasureAngular : public Gui::TaskView::TaskDialog, public Gui::SelectionObserver +{ + Q_OBJECT +public: + TaskMeasureAngular(); + ~TaskMeasureAngular(); + + virtual QDialogButtonBox::StandardButtons getStandardButtons() const + {return QDialogButtonBox::Close;} + virtual bool isAllowedAlterDocument(void) const {return false;} + virtual bool needsFullSpace() const {return false;} +protected: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +protected slots: + void selection1Slot(bool checked); + void selection2Slot(bool checked); + void resetDialogSlot(bool); + void toggle3dSlot(bool); + void toggleDeltaSlot(bool); + void clearAllSlot(bool); + void selectionClearDelayedSlot(); + +private: + void setUpGui(); + void buildDimension(); + void clearSelection(); + DimSelections selections1; + DimSelections selections2; + uint buttonSelectedIndex; + SteppedSelection *stepped; + VectorAdapter buildAdapter(const DimSelections &selection) const; +}; + +/*!start of the measure angular command*/ +void goDimensionAngularRoot(); +/*!examine angular pre selection + * @param vector1Out firt shape in current selection + * @param vector2Out second shape in current selection + * @return signal if preselection is valid. false means vector1Out and vector2Out are invalid. + */ +bool evaluateAngularPreSelection(VectorAdapter &vector1Out, VectorAdapter &vector2Out); +/*!build angular dimension*/ +void goDimensionAngularNoTask(const VectorAdapter &vector1Adapter, const VectorAdapter &vector2Adapter); +} + +#endif // TASKDIMENSION_H diff --git a/src/Mod/Part/Gui/Workbench.cpp b/src/Mod/Part/Gui/Workbench.cpp index e827fe420..f64913a7e 100644 --- a/src/Mod/Part/Gui/Workbench.cpp +++ b/src/Mod/Part/Gui/Workbench.cpp @@ -66,6 +66,11 @@ Gui::MenuItem* Workbench::setupMenuBar() const Gui::MenuItem* bop = new Gui::MenuItem; bop->setCommand("Boolean"); *bop << "Part_Boolean" << "Part_Cut" << "Part_Fuse" << "Part_Common"; + + Gui::MenuItem* measure = new Gui::MenuItem; + measure->setCommand("Measure"); + *measure << "Part_Measure_Linear" << "Part_Measure_Angular" << "Part_Measure_Clear_All" << "Part_Measure_Toggle_All" << + "Part_Measure_Toggle_3d" << "Part_Measure_Toggle_Delta"; Gui::MenuItem* part = new Gui::MenuItem; root->insertItem(item, part); @@ -74,7 +79,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *part << prim << "Part_Primitives" << "Part_Builder" << "Separator" << "Part_ShapeFromMesh" << "Part_MakeSolid" << "Part_ReverseShape" << "Part_SimpleCopy" << "Part_RefineShape" << "Part_CheckGeometry" - << "Separator" << bop << "Separator" + << measure << "Separator" << bop << "Separator" << "Part_CrossSections" << "Part_Compound" << "Part_Extrude" << "Part_Revolve" << "Part_Mirror" << "Part_Fillet" << "Part_Chamfer" << "Part_RuledSurface" << "Part_Loft" << "Part_Sweep" @@ -114,6 +119,11 @@ Gui::ToolBarItem* Workbench::setupToolBars() const boolop->setCommand("Boolean"); *boolop << "Part_Boolean" << "Part_Cut" << "Part_Fuse" << "Part_Common" << "Part_CheckGeometry" << "Part_Section" << "Part_CrossSections"; + + Gui::ToolBarItem* measure = new Gui::ToolBarItem(root); + measure->setCommand("Measure"); + *measure << "Part_Measure_Linear" << "Part_Measure_Angular" << "Part_Measure_Clear_All" << "Part_Measure_Toggle_All" + << "Part_Measure_Toggle_3d" << "Part_Measure_Toggle_Delta"; return root; } @@ -124,4 +134,3 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const Gui::ToolBarItem* root = new Gui::ToolBarItem; return root; } -