diff --git a/CMakeLists.txt b/CMakeLists.txt index 602f37de4..671b30cfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,11 @@ OPTION(FREECAD_BUILD_TEMPLATE "Build the FreeCAD template module which is only f OPTION(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF) OPTION(FREECAD_USE_EXTERNAL_ZIPIOS "Use system installed zipios++ instead of the bundled." OFF) OPTION(FREECAD_USE_EXTERNAL_PIVY "Use system installed python-pivy instead of the bundled." OFF) +if(MSVC) +OPTION(FREECAD_USE_3DCONNEXION "Use the 3D connexion SDK to support 3d mouse." ON) +else(MSVC) +set(FREECAD_USE_3DCONNEXION OFF) +endif(MSVC) # if this is set override some options if (FREECAD_BUILD_DEBIAN) @@ -206,7 +211,7 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X) if( ${OCE_FOUND} ) message("-- OpenCASCADE Community Edition has been found.") add_definitions ( -DHAVE_CONFIG_H ) - set( OCC_LIBRARIES "TKFillet;TKMesh;TKernel;TKG2d;TKG3d;TKMath;TKIGES;TKSTL;TKShHealing;TKXSBase;TKBool;TKBO;TKBRep;TKTopAlgo;TKGeomAlgo;TKGeomBase;TKOffset;TKPrim;TKSTEP;TKSTEPBase;TKSTEPAttr;TKHLR" ) #lib list copied from FreeCAD's FindOpenCasCade.cmake + set( OCC_LIBRARIES "TKFeat;TKFillet;TKMesh;TKernel;TKG2d;TKG3d;TKMath;TKIGES;TKSTL;TKShHealing;TKXSBase;TKBool;TKBO;TKBRep;TKTopAlgo;TKGeomAlgo;TKGeomBase;TKOffset;TKPrim;TKSTEP;TKSTEPBase;TKSTEPAttr;TKHLR" ) #lib list copied from FreeCAD's FindOpenCasCade.cmake set( OCC_INCLUDE_DIR ${OCE_INCLUDE_DIRS} ) set( OCC_FOUND ${OCE_FOUND} ) else() #look for OpenCASCADE @@ -219,12 +224,6 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X) ENDIF() endif() -# -------------------------------- f2c ---------------------------------- - - IF(OCC_FOUND) - find_package(F2C REQUIRED) - ENDIF(OCC_FOUND) - # -------------------------------- Salome SMESH -------------------------- # Salome SMESH sources are under src/3rdParty now @@ -268,7 +267,7 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X) # -------------------------------- ODE ---------------------------------- - find_package(ODE) +# find_package(ODE) # -------------------------------- Qt -------------------------------- @@ -439,7 +438,7 @@ if(FREECAD_MAINTAINERS_BUILD AND NOT WIN32) set(PKG_ARCH amd64) ENDIF( CMAKE_SIZEOF_VOID_P EQUAL 4 ) set(CPACK_DEBIAN_PACKAGE_SECTION "science") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "python, oce | opencascade, libqtgui4, libcoin60, libode1, libsoqt4-20, libxerces-c3.1, libgts-0.7-5, zlib1g, libboost-dev, libeigen2-dev") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "python, oce | opencascade, libqtgui4, libcoin60, libsoqt4-20, libxerces-c3.1, zlib1g, libboost-dev, libeigen2-dev") set(CPACK_PACKAGE_CONTACT "") set(CPACK_PACKAGE_FILE_NAME "${PACKAGE_NAME}-${PACKAGE_VERSION}_${PKG_ARCH}") diff --git a/cMake/FindOpenCasCade.cmake b/cMake/FindOpenCasCade.cmake index 678d4f722..1b04f709d 100644 --- a/cMake/FindOpenCasCade.cmake +++ b/cMake/FindOpenCasCade.cmake @@ -78,6 +78,7 @@ IF(OCC_LIBRARY) TKSTEPBase TKSTEPAttr TKHLR + TKFeat ) ENDIF(OCC_LIBRARY) diff --git a/cMake/UseLibPackCustom.cmake b/cMake/UseLibPackCustom.cmake index f78c9fad0..48fede470 100644 --- a/cMake/UseLibPackCustom.cmake +++ b/cMake/UseLibPackCustom.cmake @@ -329,6 +329,7 @@ set(OCC_LIBRARIES TKSTEPBase TKSTEPAttr TKHLR + TKFeat ) set(OCC_LIBRARY_DIR ${FREECAD_LIBPACK_DIR}/lib diff --git a/copying.lib b/copying.lib index 3fcf74e71..81a5194fa 100644 --- a/copying.lib +++ b/copying.lib @@ -7,8 +7,8 @@ by the authors who actually wrote it. Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. - 59 Temple Place - Suite 330 - Boston, MA 02111-1307, USA. + 51 Franklin Street - Fifth Floor + Boston, MA 02110-1301, USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/src/3rdParty/salomesmesh/CMakeLists.txt b/src/3rdParty/salomesmesh/CMakeLists.txt index 314632396..616972570 100644 --- a/src/3rdParty/salomesmesh/CMakeLists.txt +++ b/src/3rdParty/salomesmesh/CMakeLists.txt @@ -173,7 +173,7 @@ FILE(GLOB StdMeshers_source_files src/StdMeshers/*.cpp src/MEFISTO2/*.cpp src/ME INCLUDE_DIRECTORIES(src/StdMeshers) ADD_LIBRARY(StdMeshers SHARED ${StdMeshers_source_files}) -TARGET_LINK_LIBRARIES(StdMeshers SMESH TKernel TKMath TKAdvTools f2c gfortran) +TARGET_LINK_LIBRARIES(StdMeshers SMESH TKernel TKMath TKAdvTools gfortran) SET(StdMeshers_CFLAGS "") IF(WIN32) SET(StdMeshers_CFLAGS "-DSTDMESHERS_EXPORTS -DMEFISTO2D_EXPORTS") diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 17b99601f..e6a399926 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1188,13 +1188,13 @@ void Application::processCmdLineFiles(void) Application::_pcSingleton->openDocument(File.filePath().c_str()); } else if (File.hasExtension("fcscript")||File.hasExtension("fcmacro")) { - Base::Interpreter().runFile(File.filePath().c_str(), false); + Base::Interpreter().runFile(File.filePath().c_str(), true); } else if (File.hasExtension("py")) { //FIXME: Does this make any sense? I think we should do the ame as for // fcmacro or fcscript. //Base::Interpreter().loadModule(File.fileNamePure().c_str()); - Base::Interpreter().runFile(File.filePath().c_str(), false); + Base::Interpreter().runFile(File.filePath().c_str(), true); } else { std::vector mods = App::GetApplication().getImportModules(Ext.c_str()); @@ -1210,8 +1210,7 @@ void Application::processCmdLineFiles(void) } } catch (const Base::SystemExitException&) { - Base::PyGILStateLocker locker; - Base::Interpreter().systemExit(); + throw; // re-throw to main() function } catch (const Base::Exception& e) { Console().Error("Exception while processing file: %s [%s]\n", File.filePath().c_str(), e.what()); @@ -1523,20 +1522,23 @@ void Application::ParseOptions(int ac, char ** av) notify(vm); } catch (const std::exception& e) { - cerr << e.what() << endl << endl << visible << endl; - exit(1); + std::stringstream str; + str << e.what() << endl << endl << visible << endl; + throw UnknownProgramOption(str.str()); } catch (...) { - cerr << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl; - exit(1); + std::stringstream str; + str << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl; + throw UnknownProgramOption(str.str()); } if (vm.count("help")) { - cout << mConfig["ExeName"] << endl << endl; - cout << "For detailed descripton see http://free-cad.sf.net" << endl<().c_str()); if (!ifs) { Base::Console().Error("Could no open the response file\n"); - cerr << "Could no open the response file: '" - << vm["response-file"].as() << "'" << endl; - exit(1); + std::stringstream str; + str << "Could no open the response file: '" + << vm["response-file"].as() << "'" << endl; + throw Base::UnknownProgramOption(str.str()); } // Read the whole file into a string stringstream ss; @@ -1562,9 +1565,10 @@ void Application::ParseOptions(int ac, char ** av) } if (vm.count("version")) { - std::cout << mConfig["ExeName"] << " " << mConfig["ExeVersion"] - << " Revision: " << mConfig["BuildRevision"] << std::endl; - exit(0); + std::stringstream str; + str << mConfig["ExeName"] << " " << mConfig["ExeVersion"] + << " Revision: " << mConfig["BuildRevision"] << std::endl; + throw Base::ProgramInformation(str.str()); } if (vm.count("console")) { diff --git a/src/App/PropertyPythonObject.cpp b/src/App/PropertyPythonObject.cpp index c335dabf5..aa5534e61 100644 --- a/src/App/PropertyPythonObject.cpp +++ b/src/App/PropertyPythonObject.cpp @@ -35,6 +35,7 @@ #include #include #include +#include using namespace App; @@ -82,10 +83,20 @@ std::string PropertyPythonObject::toString() const std::string repr; Base::PyGILStateLocker lock; try { - Py::Module pickle(PyImport_ImportModule("cPickle"),true); + Py::Module pickle(PyImport_ImportModule("json"),true); Py::Callable method(pickle.getAttr(std::string("dumps"))); + Py::Object dump; + if (this->object.hasAttr("__getstate__")) { + Py::Tuple args(0); + Py::Callable state(this->object.getAttr("__getstate__")); + dump = state.apply(args); + } + else if (this->object.hasAttr("__dict__")) { + dump = this->object.getAttr("__dict__"); + } + Py::Tuple args(1); - args.setItem(0, this->object); + args.setItem(0, dump); Py::Object res = method.apply(args); Py::String str(res); repr = str.as_std_string(); @@ -102,12 +113,21 @@ void PropertyPythonObject::fromString(const std::string& repr) { Base::PyGILStateLocker lock; try { - Py::Module pickle(PyImport_ImportModule("cPickle"),true); + Py::Module pickle(PyImport_ImportModule("json"),true); Py::Callable method(pickle.getAttr(std::string("loads"))); Py::Tuple args(1); args.setItem(0, Py::String(repr)); Py::Object res = method.apply(args); - this->object = res; + + if (this->object.hasAttr("__setstate__")) { + Py::Tuple args(1); + args.setItem(0, res); + Py::Callable state(this->object.getAttr("__setstate__")); + state.apply(args); + } + else { + this->object.setAttr("__dict__", res); + } } catch (Py::Exception&) { Base::PyException e; // extract the Python error text @@ -115,6 +135,32 @@ void PropertyPythonObject::fromString(const std::string& repr) } } +void PropertyPythonObject::loadPickle(const std::string& str) +{ + // find the custom attributes and restore them + Base::PyGILStateLocker lock; + try { + std::string buffer = str; + boost::regex pickle("S'(\\w+)'.+S'(\\w+)'\\n"); + boost::match_results what; + std::string::const_iterator start, end; + start = buffer.begin(); + end = buffer.end(); + while (boost::regex_search(start, end, what, pickle)) { + std::string key = std::string(what[1].first, what[1].second); + std::string val = std::string(what[2].first, what[2].second); + this->object.setAttr(key, Py::String(val)); + buffer = std::string(what[2].second, end); + start = buffer.begin(); + end = buffer.end(); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + Base::Console().Warning("PropertyPythonObject::loadPickle: %s\n", e.what()); + } +} + std::string PropertyPythonObject::encodeValue(const std::string& str) const { std::string tmp; @@ -210,7 +256,25 @@ void PropertyPythonObject::Save (Base::Writer &writer) const repr = Base::base64_encode((const unsigned char*)repr.c_str(), repr.size()); std::string val = /*encodeValue*/(repr); writer.Stream() << writer.ind() << "object.hasAttr("__module__") && this->object.hasAttr("__class__")) { + Py::String mod(this->object.getAttr("__module__")); + Py::Object cls(this->object.getAttr("__class__")); + if (cls.hasAttr("__name__")) { + Py::String name(cls.getAttr("__name__")); + writer.Stream() << " module=\"" << (std::string)mod << "\"" + << " class=\"" << (std::string)name << "\""; + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + Base::Console().Warning("PropertyPythonObject::Save: %s\n", e.what()); + } + saveObject(writer); writer.Stream() << "/>" << std::endl; //} @@ -228,6 +292,8 @@ void PropertyPythonObject::Restore(Base::XMLReader &reader) reader.addFile(file.c_str(),this); } else { + bool load_json=false; + bool load_pickle=false; std::string buffer = reader.getAttribute("value"); if (reader.hasAttribute("encoded") && strcmp(reader.getAttribute("encoded"),"yes") == 0) { @@ -237,8 +303,39 @@ void PropertyPythonObject::Restore(Base::XMLReader &reader) buffer = decodeValue(buffer); } + Base::PyGILStateLocker lock; + try { + boost::regex pickle("^\\(i(\\w+)\\n(\\w+)\\n"); + boost::match_results what; + std::string::const_iterator start, end; + start = buffer.begin(); + end = buffer.end(); + if (reader.hasAttribute("module") && reader.hasAttribute("class")) { + Py::Module mod(PyImport_ImportModule(reader.getAttribute("module")),true); + this->object = PyInstance_NewRaw(mod.getAttr(reader.getAttribute("class")).ptr(), 0); + load_json = true; + } + else if (boost::regex_search(start, end, what, pickle)) { + std::string nam = std::string(what[1].first, what[1].second); + std::string cls = std::string(what[2].first, what[2].second); + Py::Module mod(PyImport_ImportModule(nam.c_str()),true); + this->object = PyInstance_NewRaw(mod.getAttr(cls).ptr(), 0); + load_pickle = true; + buffer = std::string(what[2].second, end); + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + Base::Console().Warning("PropertyPythonObject::Restore: %s\n", e.what()); + } + aboutToSetValue(); - this->fromString(buffer); + if (load_json) + this->fromString(buffer); + else if (load_pickle) + this->loadPickle(buffer); + else + Base::Console().Warning("PropertyPythonObject::Restore: unsupported serialisation: %s\n", buffer.c_str()); restoreObject(reader); hasSetValue(); } diff --git a/src/App/PropertyPythonObject.h b/src/App/PropertyPythonObject.h index 381929995..bd4bec933 100644 --- a/src/App/PropertyPythonObject.h +++ b/src/App/PropertyPythonObject.h @@ -77,6 +77,7 @@ private: void restoreObject(Base::XMLReader &reader); std::string encodeValue(const std::string& str) const; std::string decodeValue(const std::string& str) const; + void loadPickle(const std::string& str); Py::Object object; }; diff --git a/src/Base/Exception.cpp b/src/Base/Exception.cpp index e573c2735..4ca62890c 100644 --- a/src/Base/Exception.cpp +++ b/src/Base/Exception.cpp @@ -202,6 +202,40 @@ AbnormalProgramTermination::AbnormalProgramTermination(const AbnormalProgramTerm // --------------------------------------------------------- +UnknownProgramOption::UnknownProgramOption(const char * sMessage) + : Exception(sMessage) +{ +} + +UnknownProgramOption::UnknownProgramOption(const std::string& sMessage) + : Exception(sMessage) +{ +} + +UnknownProgramOption::UnknownProgramOption(const UnknownProgramOption &inst) + : Exception(inst) +{ +} + +// --------------------------------------------------------- + +ProgramInformation::ProgramInformation(const char * sMessage) + : Exception(sMessage) +{ +} + +ProgramInformation::ProgramInformation(const std::string& sMessage) + : Exception(sMessage) +{ +} + +ProgramInformation::ProgramInformation(const ProgramInformation &inst) + : Exception(inst) +{ +} + +// --------------------------------------------------------- + #if defined(__GNUC__) && defined (FC_OS_LINUX) #include #include diff --git a/src/Base/Exception.h b/src/Base/Exception.h index f50fdb7d3..19bd9155c 100644 --- a/src/Base/Exception.h +++ b/src/Base/Exception.h @@ -174,6 +174,38 @@ public: virtual ~AbnormalProgramTermination() throw() {} }; +/** + * The UnknownProgramOption can be used to indicate an unknown program option. + * @author Werner Mayer + */ +class BaseExport UnknownProgramOption : public Exception +{ +public: + /// Construction + UnknownProgramOption(const char * sMessage); + UnknownProgramOption(const std::string& sMessage); + /// Construction + UnknownProgramOption(const UnknownProgramOption &inst); + /// Destruction + virtual ~UnknownProgramOption() throw() {} +}; + +/** + * The ProgramInformation can be used to show information about the program. + * @author Werner Mayer + */ +class BaseExport ProgramInformation : public Exception +{ +public: + /// Construction + ProgramInformation(const char * sMessage); + ProgramInformation(const std::string& sMessage); + /// Construction + ProgramInformation(const ProgramInformation &inst); + /// Destruction + virtual ~ProgramInformation() throw() {} +}; + inline void Exception::setMessage(const char * sMessage) { diff --git a/src/Base/Interpreter.cpp b/src/Base/Interpreter.cpp index 0a8c4b9e8..a1ddd5a7a 100644 --- a/src/Base/Interpreter.cpp +++ b/src/Base/Interpreter.cpp @@ -190,7 +190,6 @@ void InterpreterSingleton::runInteractiveString(const char *sCmd) presult = PyRun_String(sCmd, Py_single_input, dict, dict); /* eval direct */ if (!presult) { if (PyErr_ExceptionMatches(PyExc_SystemExit)) { - //systemExit(); throw SystemExitException(); } /* get latest python exception information */ diff --git a/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp b/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp new file mode 100644 index 000000000..425a7af7a --- /dev/null +++ b/src/Gui/3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp @@ -0,0 +1,718 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + +/* +Support for Qt added by David Dibben at +http://www.codegardening.com/2011/02/using-3dconnexion-mouse-with-qt.html +*/ + +/* +See also: +http://www.3dconnexion.com/forum/viewtopic.php?f=19&t=4968&sid=72c018bdcf0e6edc99a6effb5c0c48d9 +*/ + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include "GuiApplicationNativeEventAware.h" +#include "SpaceballEvent.h" + +// Windows dependencies, enumerators and global variables +#ifdef _USE_3DCONNEXION_SDK + +#include +#include +#include + +#define LOGITECH_VENDOR_ID 0x46d +#define _CONSTANT_INPUT_PERIOD 0 + +#ifndef RIDEV_DEVNOTIFY +#define RIDEV_DEVNOTIFY 0x00002000 +#endif + +#define _TRACE_WM_INPUT_PERIOD 0 +#define _TRACE_RI_TYPE 0 +#define _TRACE_RIDI_DEVICENAME 0 +#define _TRACE_RIDI_DEVICEINFO 0 +#define _TRACE_RI_RAWDATA 0 +#define _TRACE_3DINPUT_PERIOD 0 + +#ifdef _WIN64 +typedef unsigned __int64 QWORD; +#endif // _WIN64 + +static const int kTimeToLive = 5; + +enum e3dconnexion_pid { + eSpacePilot = 0xc625, + eSpaceNavigator = 0xc626, + eSpaceExplorer = 0xc627, + eSpaceNavigatorForNotebooks = 0xc628, + eSpacePilotPRO = 0xc629 +}; + +enum e3dmouse_virtual_key +{ + V3DK_INVALID=0 + , V3DK_MENU=1, V3DK_FIT + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, V3DK_BOTTOM, V3DK_BACK + , V3DK_CW, V3DK_CCW + , V3DK_ISO1, V3DK_ISO2 + , V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, V3DK_7, V3DK_8, V3DK_9, V3DK_10 + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_ROTATE, V3DK_PANZOOM, V3DK_DOMINANT + , V3DK_PLUS, V3DK_MINUS +}; + +struct tag_VirtualKeys +{ + e3dconnexion_pid pid; + size_t nKeys; + e3dmouse_virtual_key *vkeys; +}; + +static const e3dmouse_virtual_key SpaceExplorerKeys [] = +{ + V3DK_INVALID // there is no button 0 + , V3DK_1, V3DK_2 + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_FIT, V3DK_MENU + , V3DK_PLUS, V3DK_MINUS + , V3DK_ROTATE +}; + +static const e3dmouse_virtual_key SpacePilotKeys [] = +{ + V3DK_INVALID + , V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6 + , V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT + , V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL + , V3DK_FIT, V3DK_MENU + , V3DK_PLUS, V3DK_MINUS + , V3DK_DOMINANT, V3DK_ROTATE +}; + +static const struct tag_VirtualKeys _3dmouseVirtualKeys[]= +{ + eSpacePilot + , sizeof(SpacePilotKeys)/sizeof(SpacePilotKeys[0]) + , const_cast(SpacePilotKeys), + eSpaceExplorer + , sizeof(SpaceExplorerKeys)/sizeof(SpaceExplorerKeys[0]) + , const_cast(SpaceExplorerKeys) +}; + + +// Methods for windows events + +/*! + Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device to the standard 3d mouse virtual key definition. + + \a pid USB Product ID (PID) of 3D mouse device + \a hidKeyCode Hid keycode as retrieved from a Raw Input packet + + \return The standard 3d mouse virtual key (button identifier) or zero if an error occurs. + + Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device + to the standard 3d mouse virtual key definition. +*/ + +unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) +{ + unsigned short virtualkey=hidKeyCode; + for (size_t i=0; imessage == WM_INPUT) { + HRAWINPUT hRawInput = reinterpret_cast(message->lParam); + gMouseInput->OnRawInput(RIM_INPUT,hRawInput); + if (result != 0) { + result = 0; + } + return true; + } + + return false; +} + + +/*! + Access the mouse parameters structure +*/ +I3dMouseParam& Gui::GUIApplicationNativeEventAware::MouseParams() +{ + return f3dMouseParams; +} + +/*! + Access the mouse parameters structure +*/ +const I3dMouseParam& Gui::GUIApplicationNativeEventAware::MouseParams() const +{ + return f3dMouseParams; +} + +/*! + Called with the processed motion data when a 3D mouse event is received + + The default implementation emits a Move3d signal with the motion data +*/ +void Gui::GUIApplicationNativeEventAware::Move3d(HANDLE device, std::vector& motionData) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + int x, y, z, rx, ry, rz; + x = ceil(motionData[0])*(-1); + y = ceil(motionData[1]); + z = ceil(motionData[2]); + rx = ceil(motionData[3])*(-1); + ry = ceil(motionData[4]); + rz = ceil(motionData[5]); + + Spaceball::MotionEvent *motionEvent = new Spaceball::MotionEvent(); + motionEvent->setTranslations(x, y, z); + motionEvent->setRotations(rx, ry, rz); + this->postEvent(currentWidget, motionEvent); +} + +/*! + Called when a 3D mouse key is pressed + + The default implementation emits a On3dmouseKeyDown signal with the key code. +*/ +void Gui::GUIApplicationNativeEventAware::On3dmouseKeyDown(HANDLE device, int virtualKeyCode) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent(); + buttonEvent->setButtonNumber(virtualKeyCode - 1); + buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED); + this->postEvent(currentWidget, buttonEvent); +} + +/*! + Called when a 3D mouse key is released + + The default implementation emits a On3dmouseKeyUp signal with the key code. +*/ +void Gui::GUIApplicationNativeEventAware::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) +{ + Q_UNUSED(device); + + QWidget *currentWidget = this->focusWidget(); + if (!currentWidget) + currentWidget = mainWindow; + + Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent(); + buttonEvent->setButtonNumber(virtualKeyCode - 1); + buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED); + this->postEvent(currentWidget, buttonEvent); +} + +/*! + Get an initialized array of PRAWINPUTDEVICE for the 3D devices + + pNumDevices returns the number of devices to register. Currently this is always 1. + */ +static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) +{ + // Array of raw input devices to register + static RAWINPUTDEVICE sRawInputDevices[] = { + {0x01, 0x08, 0x00, 0x00} // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller + }; + + if (pNumDevices) { + *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); + } + + return sRawInputDevices; +} + +/*! + Detect the 3D mouse +*/ +bool Gui::GUIApplicationNativeEventAware::Is3dmouseAttached() +{ + unsigned int numDevicesOfInterest = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); + + unsigned int nDevices = 0; + + if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { + return false; + } + + if (nDevices == 0) return false; + + std::vector rawInputDeviceList(nDevices); + if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { + return false; + } + + for (unsigned int i = 0; i < nDevices; ++i) { + RID_DEVICE_INFO rdi = {sizeof(rdi)}; + unsigned int cbSize = sizeof(rdi); + + if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { + //skip non HID and non logitec (3DConnexion) devices + if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { + continue; + } + + //check if devices matches Multi-axis Controller + for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { + if (devicesToRegister[j].usUsage == rdi.hid.usUsage + && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { + return true; + } + } + } + } + return false; +} + + + +/*! + Initialize the window to recieve raw-input messages + + This needs to be called initially so that Windows will send the messages from the 3D mouse to the window. +*/ +bool Gui::GUIApplicationNativeEventAware::InitializeRawInput(HWND hwndTarget) +{ + fWindow = hwndTarget; + + // Simply fail if there is no window + if (!hwndTarget) return false; + + unsigned int numDevices = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevices); + + if (numDevices == 0) return false; + + // Get OS version. + OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO),0}; + ::GetVersionEx(&osvi); + + unsigned int cbSize = sizeof (devicesToRegister[0]); + for (size_t i = 0; i < numDevices; i++) { + // Set the target window to use + //devicesToRegister[i].hwndTarget = hwndTarget; + + // If Vista or newer, enable receiving the WM_INPUT_DEVICE_CHANGE message. + if (osvi.dwMajorVersion >= 6) { + devicesToRegister[i].dwFlags |= RIDEV_DEVNOTIFY; + } + } + return (::RegisterRawInputDevices(devicesToRegister, numDevices, cbSize) != FALSE); +} + + +/*! + Get the raw input data from Windows + + Includes workaround for incorrect alignment of the RAWINPUT structure on x64 os + when running as Wow64 (copied directly from 3DConnexion code) +*/ + +UINT Gui::GUIApplicationNativeEventAware::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader) +{ +#ifdef _WIN64 + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); +#else + BOOL bIsWow64 = FALSE; + ::IsWow64Process(GetCurrentProcess(), &bIsWow64); + if (!bIsWow64 || pData==NULL) { + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); + } else { + HWND hwndTarget = fWindow; //fParent->winId(); + + size_t cbDataSize=0; + UINT nCount=0; + PRAWINPUT pri = pData; + + MSG msg; + while (PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_NOREMOVE)) { + HRAWINPUT hRawInput = reinterpret_cast(msg.lParam); + size_t cbSize = *pcbSize - cbDataSize; + if (::GetRawInputData(hRawInput, RID_INPUT, pri, &cbSize, cbSizeHeader) == static_cast(-1)) { + if (nCount==0) { + return static_cast(-1); + } else { + break; + } + } + ++nCount; + + // Remove the message for the data just read + PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_REMOVE); + + pri = NEXTRAWINPUTBLOCK(pri); + cbDataSize = reinterpret_cast(pri) - reinterpret_cast(pData); + if (cbDataSize >= *pcbSize) { + cbDataSize = *pcbSize; + break; + } + } + return nCount; + } +#endif +} + +/*! + Process the raw input device data + + On3dmouseInput() does all the preprocessing of the rawinput device data before + finally calling the Move3d method. +*/ + +void Gui::GUIApplicationNativeEventAware::On3dmouseInput() +{ + // Don't do any data processing in background + bool bIsForeground = (::GetActiveWindow() != NULL); + if (!bIsForeground) { + // set all cached data to zero so that a zero event is seen and the cached data deleted + for (std::map::iterator it = fDevice2Data.begin(); it != fDevice2Data.end(); it++) { + it->second.fAxes.assign(6, .0); + it->second.fIsDirty = true; + } + } + + DWORD dwNow = ::GetTickCount(); // Current time; + DWORD dwElapsedTime; // Elapsed time since we were last here + + if (0 == fLast3dmouseInputTime) { + dwElapsedTime = 10; // System timer resolution + } else { + dwElapsedTime = dwNow - fLast3dmouseInputTime; + if (fLast3dmouseInputTime > dwNow) { + dwElapsedTime = ~dwElapsedTime+1; + } + if (dwElapsedTime<1) { + dwElapsedTime=1; + } else if (dwElapsedTime > 500) { + // Check for wild numbers because the device was removed while sending data + dwElapsedTime = 10; + } + } + +#if _TRACE_3DINPUT_PERIOD + qDebug("On3DmouseInput() period is %dms\n", dwElapsedTime); +#endif + + float mouseData2Rotation; + // v = w * r, we don't know r yet so lets assume r=1.) + float mouseData2PanZoom; + + // Grab the I3dmouseParam interface + I3dMouseParam& i3dmouseParam = f3dMouseParams; + // Take a look at the users preferred speed setting and adjust the sensitivity accordingly + I3dMouseSensor::ESpeed speedSetting = i3dmouseParam.GetSpeed(); + // See "Programming for the 3D Mouse", Section 5.1.3 + float speed = (speedSetting == I3dMouseSensor::kLowSpeed ? 0.25f : speedSetting == I3dMouseSensor::kHighSpeed ? 4.f : 1.f); + + // Multiplying by the following will convert the 3d mouse data to real world units + mouseData2PanZoom = speed; + mouseData2Rotation = speed; + + std::map::iterator iterator=fDevice2Data.begin(); + while (iterator != fDevice2Data.end()) { + + // If we have not received data for a while send a zero event + if ((--(iterator->second.fTimeToLive)) == 0) { + iterator->second.fAxes.assign(6, .0); + } else if (/*!t_bPoll3dmouse &&*/ !iterator->second.fIsDirty) { + // If we are not polling then only handle the data that was actually received + ++iterator; + continue; + } + iterator->second.fIsDirty=false; + + // get a copy of the device + HANDLE hdevice = iterator->first; + + // get a copy of the motion vectors and apply the user filters + std::vector motionData = iterator->second.fAxes; + + // apply the user filters + + // Pan Zoom filter + // See "Programming for the 3D Mouse", Section 5.1.2 + if (!i3dmouseParam.IsPanZoom()) { + // Pan zoom is switched off so set the translation vector values to zero + motionData[0] = motionData[1] = motionData[2] = 0.; + } + + // Rotate filter + // See "Programming for the 3D Mouse", Section 5.1.1 + if (!i3dmouseParam.IsRotate()) { + // Rotate is switched off so set the rotation vector values to zero + motionData[3] = motionData[4] = motionData[5] = 0.; + } + + // convert the translation vector into physical data + for (int axis = 0; axis < 3; axis++) { + motionData[axis] *= mouseData2PanZoom; + } + // convert the directed Rotate vector into physical data + // See "Programming for the 3D Mouse", Section 7.2.2 + for (int axis = 3; axis < 6; axis++) { + motionData[axis] *= mouseData2Rotation; + } + + // Now that the data has had the filters and sensitivty settings applied + // calculate the displacements since the last view update + for (int axis = 0; axis < 6; axis++) { + motionData[axis] *= dwElapsedTime; + } + + + // Now a bit of book keeping before passing on the data + if (iterator->second.IsZero()) { + iterator = fDevice2Data.erase(iterator); + } else { + ++iterator; + } + + // Work out which will be the next device + HANDLE hNextDevice = 0; + if (iterator != fDevice2Data.end()) { + hNextDevice = iterator->first; + } + + // Pass the 3dmouse input to the view controller + Move3d(hdevice, motionData); + + // Because we don't know what happened in the previous call, the cache might have + // changed so reload the iterator + iterator = fDevice2Data.find(hNextDevice); + } + + if (!fDevice2Data.empty()) { + fLast3dmouseInputTime = dwNow; + } else { + fLast3dmouseInputTime = 0; + } +} + +/*! + Called when new raw input data is available +*/ +void Gui::GUIApplicationNativeEventAware::OnRawInput(UINT nInputCode, HRAWINPUT hRawInput) +{ + const size_t cbSizeOfBuffer=1024; + BYTE pBuffer[cbSizeOfBuffer]; + + PRAWINPUT pRawInput = reinterpret_cast(pBuffer); + UINT cbSize = cbSizeOfBuffer; + + if (::GetRawInputData(hRawInput, RID_INPUT, pRawInput, &cbSize, sizeof(RAWINPUTHEADER)) == static_cast(-1)) { + return; + } + + bool b3dmouseInput = TranslateRawInputData(nInputCode, pRawInput); + ::DefRawInputProc(&pRawInput, 1, sizeof(RAWINPUTHEADER)); + + // Check for any buffered messages + cbSize = cbSizeOfBuffer; + UINT nCount = this->GetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + if (nCount == (UINT)-1) { + qDebug ("GetRawInputBuffer returned error %d\n", GetLastError()); + } + + while (nCount>0 && nCount != static_cast(-1)) { + PRAWINPUT pri = pRawInput; + UINT nInput; + for (nInput=0; nInputGetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + } + + // If we have mouse input data for the app then tell tha app about it + if (b3dmouseInput) { + On3dmouseInput(); + } +} + + + +bool Gui::GUIApplicationNativeEventAware::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput) +{ + bool bIsForeground = (nInputCode == RIM_INPUT); + +#if _TRACE_RI_TYPE + qDebug("Rawinput.header.dwType=0x%x\n", pRawInput->header.dwType); +#endif + // We are not interested in keyboard or mouse data received via raw input + if (pRawInput->header.dwType != RIM_TYPEHID) return false; + +#if _TRACE_RIDI_DEVICENAME + UINT dwSize=0; + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, NULL, &dwSize) == 0) { + std::vector szDeviceName(dwSize+1); + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, &szDeviceName[0], &dwSize) >0) { + qDebug("Device Name = %s\nDevice handle = 0x%x\n", &szDeviceName[0], pRawInput->header.hDevice); + } + } +#endif + + RID_DEVICE_INFO sRidDeviceInfo; + sRidDeviceInfo.cbSize = sizeof(RID_DEVICE_INFO); + UINT cbSize = sizeof(RID_DEVICE_INFO); + + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &sRidDeviceInfo, &cbSize) == cbSize) { +#if _TRACE_RIDI_DEVICEINFO + switch (sRidDeviceInfo.dwType) { + case RIM_TYPEMOUSE: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEMOUSE\n"); + break; + case RIM_TYPEKEYBOARD: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEKEYBOARD\n"); + break; + case RIM_TYPEHID: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEHID\n"); + qDebug("\tVendor=0x%x\n\tProduct=0x%x\n\tUsagePage=0x%x\n\tUsage=0x%x\n", + sRidDeviceInfo.hid.dwVendorId, + sRidDeviceInfo.hid.dwProductId, + sRidDeviceInfo.hid.usUsagePage, + sRidDeviceInfo.hid.usUsage); + break; + } +#endif + + if (sRidDeviceInfo.hid.dwVendorId == LOGITECH_VENDOR_ID) { + if (pRawInput->data.hid.bRawData[0] == 0x01) { // Translation vector + + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + if (bIsForeground) { + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the pan zoom data + deviceData.fAxes[0] = static_cast(pnRawData[0]); + deviceData.fAxes[1] = static_cast(pnRawData[1]); + deviceData.fAxes[2] = static_cast(pnRawData[2]); + +#if _TRACE_RI_RAWDATA + qDebug("Pan/Zoom RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[0], pnRawData[1], pnRawData[2]); +#endif + if (pRawInput->data.hid.dwSizeHid >= 13) {// Highspeed package + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[3]); + deviceData.fAxes[4] = static_cast(pnRawData[4]); + deviceData.fAxes[5] = static_cast(pnRawData[5]); + deviceData.fIsDirty = true; +#if _TRACE_RI_RAWDATA + qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[3], pnRawData[4], pnRawData[5]); +#endif + return true; + } + } else { // Zero out the data if the app is not in forground + deviceData.fAxes.assign(6, 0.f); + } + } else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector + // If we are not in foreground do nothing + // The rotation vector was zeroed out with the translation vector in the previous message + if (bIsForeground) { + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[0]); + deviceData.fAxes[4] = static_cast(pnRawData[1]); + deviceData.fAxes[5] = static_cast(pnRawData[2]); + deviceData.fIsDirty = true; + +#if _TRACE_RI_RAWDATA + qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", + pnRawData[0], pnRawData[1], pnRawData[2]); +#endif + return true; + } + } else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change + // this is a package that contains 3d mouse keystate information + // bit0=key1, bit=key2 etc. + + + unsigned long dwKeystate = *reinterpret_cast(&pRawInput->data.hid.bRawData[1]); +#if _TRACE_RI_RAWDATA + qDebug("ButtonData =0x%x\n", dwKeystate); +#endif + // Log the keystate changes + unsigned long dwOldKeystate = fDevice2Keystate[pRawInput->header.hDevice]; + if (dwKeystate != 0) { + fDevice2Keystate[pRawInput->header.hDevice] = dwKeystate; + } else { + fDevice2Keystate.erase(pRawInput->header.hDevice); + } + + // Only call the keystate change handlers if the app is in foreground + if (bIsForeground) { + unsigned long dwChange = dwKeystate ^ dwOldKeystate; + + + for (int nKeycode=1; nKeycode<33; nKeycode++) { + if (dwChange & 0x01) { + int nVirtualKeyCode = HidToVirtualKey(sRidDeviceInfo.hid.dwProductId, nKeycode); + if (nVirtualKeyCode) { + if (dwKeystate&0x01) { + On3dmouseKeyDown(pRawInput->header.hDevice, nVirtualKeyCode); + } else { + On3dmouseKeyUp(pRawInput->header.hDevice, nVirtualKeyCode); + } + } + } + dwChange >>=1; + dwKeystate >>=1; + } + } + } + } + } + return false; +} +#endif // _USE_3DCONNEXION_SDK diff --git a/src/Gui/3Dconnexion/I3dMouseParams.h b/src/Gui/3Dconnexion/I3dMouseParams.h new file mode 100644 index 000000000..62d3072d8 --- /dev/null +++ b/src/Gui/3Dconnexion/I3dMouseParams.h @@ -0,0 +1,91 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + +#ifndef I3D_MOUSE_PARAMS_H +#define I3D_MOUSE_PARAMS_H + +/* + Parameters for the 3D mouse based on the SDK from 3Dconnexion +*/ + +class I3dMouseSensor +{ +public: + + enum ESpeed { + kLowSpeed = 0, + kMidSpeed, + kHighSpeed + }; + +virtual bool IsPanZoom() const = 0; +virtual bool IsRotate() const = 0; +virtual ESpeed GetSpeed() const = 0; + +virtual void SetPanZoom(bool isPanZoom) = 0; +virtual void SetRotate(bool isRotate) = 0; +virtual void SetSpeed(ESpeed speed) = 0; + +protected: + +virtual ~I3dMouseSensor() {} + +}; + + +class I3dMouseNavigation +{ +public: + + enum EPivot { + kManualPivot = 0, + kAutoPivot, + kAutoPivotOverride + }; + + enum ENavigation { + kObjectMode = 0, + kCameraMode, + kFlyMode, + kWalkMode, + kHelicopterMode + }; + + enum EPivotVisibility { + kHidePivot = 0, + kShowPivot, + kShowMovingPivot + }; + + +virtual ENavigation GetNavigationMode() const = 0; +virtual EPivot GetPivotMode() const = 0; +virtual EPivotVisibility GetPivotVisibility() const = 0; +virtual bool IsLockHorizon() const = 0; + +virtual void SetLockHorizon(bool bOn) = 0; +virtual void SetNavigationMode(ENavigation navigation) = 0; +virtual void SetPivotMode(EPivot pivot) = 0; +virtual void SetPivotVisibility(EPivotVisibility visibility) = 0; + +protected: + +virtual ~I3dMouseNavigation(){} + + +}; + + +class I3dMouseParam : public I3dMouseSensor, public I3dMouseNavigation +{ +public: + +virtual ~I3dMouseParam() {} + +}; + +#endif // I3D_MOUSE_PARAMS_H + diff --git a/src/Gui/3Dconnexion/MouseParameters.cpp b/src/Gui/3Dconnexion/MouseParameters.cpp new file mode 100644 index 000000000..5a822fded --- /dev/null +++ b/src/Gui/3Dconnexion/MouseParameters.cpp @@ -0,0 +1,95 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + + +#include "PreCompiled.h" +#include "MouseParameters.h" + +MouseParameters::MouseParameters(): fNavigation(kObjectMode) + , fPivot(kAutoPivot) + , fPivotVisibility(kShowPivot) + , fIsLockHorizon(true) + , fIsPanZoom(true) + , fIsRotate(true) + , fSpeed(kLowSpeed) +{ +} + +MouseParameters::~MouseParameters() +{ +} + +bool MouseParameters::IsPanZoom() const +{ + return fIsPanZoom; +} + +bool MouseParameters::IsRotate() const +{ + return fIsRotate; +} + +MouseParameters::ESpeed MouseParameters::GetSpeed() const +{ + return fSpeed; +} + +void MouseParameters::SetPanZoom(bool isPanZoom) +{ + fIsPanZoom=isPanZoom; +} + +void MouseParameters::SetRotate(bool isRotate) +{ + fIsRotate=isRotate; +} + +void MouseParameters::SetSpeed(ESpeed speed) +{ + fSpeed=speed; +} + + +MouseParameters::ENavigation MouseParameters::GetNavigationMode() const +{ + return fNavigation; +} + +MouseParameters::EPivot MouseParameters::GetPivotMode() const +{ + return fPivot; +} + +MouseParameters::EPivotVisibility MouseParameters::GetPivotVisibility() const +{ + return fPivotVisibility; +} + +bool MouseParameters::IsLockHorizon() const +{ + return fIsLockHorizon; +} + +void MouseParameters::SetLockHorizon(bool bOn) +{ + fIsLockHorizon=bOn; +} + +void MouseParameters::SetNavigationMode(ENavigation navigation) +{ + fNavigation=navigation; +} + +void MouseParameters::SetPivotMode(EPivot pivot) +{ + if (fPivot!=kManualPivot || pivot!=kAutoPivotOverride) + fPivot = pivot; +} + +void MouseParameters::SetPivotVisibility(EPivotVisibility visibility) +{ + fPivotVisibility = visibility; +} diff --git a/src/Gui/3Dconnexion/MouseParameters.h b/src/Gui/3Dconnexion/MouseParameters.h new file mode 100644 index 000000000..3e00701bf --- /dev/null +++ b/src/Gui/3Dconnexion/MouseParameters.h @@ -0,0 +1,56 @@ + +/* +Development tools and related technology provided under license from 3Dconnexion. +(c) 1992 - 2012 3Dconnexion. All rights reserved +*/ + + +#ifndef T3D_MOUSE_PARAMS_H +#define T3D_MOUSE_PARAMS_H + +#include "I3dMouseParams.h" + + +class MouseParameters : public I3dMouseParam +{ +public: + MouseParameters(); + ~MouseParameters(); + + // I3dmouseSensor interface + bool IsPanZoom() const; + bool IsRotate() const; + ESpeed GetSpeed() const; + + void SetPanZoom(bool isPanZoom); + void SetRotate(bool isRotate); + void SetSpeed(ESpeed speed); + + // I3dmouseNavigation interface + ENavigation GetNavigationMode() const; + EPivot GetPivotMode() const; + EPivotVisibility GetPivotVisibility() const; + bool IsLockHorizon() const; + + void SetLockHorizon(bool bOn); + void SetNavigationMode(ENavigation navigation); + void SetPivotMode(EPivot pivot); + void SetPivotVisibility(EPivotVisibility visibility); + +private: + + MouseParameters (const MouseParameters&); + const MouseParameters& operator =(const MouseParameters&); + + ENavigation fNavigation; + EPivot fPivot; + EPivotVisibility fPivotVisibility; + bool fIsLockHorizon; + + bool fIsPanZoom; + bool fIsRotate; + ESpeed fSpeed; +}; + +#endif // T3D_MOUSE_PARAMS_H + diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index ad5663902..5fdc7ed29 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1545,8 +1545,16 @@ void Application::runApplication(void) int argc = App::Application::GetARGC(); GUIApplication mainApp(argc, App::Application::GetARGV()); // set application icon and window title + const std::map& cfg = App::Application::Config(); + std::map::const_iterator it; + it = cfg.find("Application"); + if (it != cfg.end()) { + mainApp.setApplicationName(QString::fromUtf8(it->second.c_str())); + } + else { + mainApp.setApplicationName(QString::fromUtf8(App::GetApplication().getExecutableName())); + } mainApp.setWindowIcon(Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str())); - mainApp.setApplicationName(QString::fromAscii(App::GetApplication().getExecutableName())); QString plugin; plugin = QString::fromUtf8(App::GetApplication().GetHomePath()); plugin += QLatin1String("/plugins"); @@ -1606,8 +1614,6 @@ void Application::runApplication(void) QString home = QString::fromUtf8(App::GetApplication().GetHomePath()); - const std::map& cfg = App::Application::Config(); - std::map::const_iterator it; it = cfg.find("WindowTitle"); if (it != cfg.end()) { QString title = QString::fromUtf8(it->second.c_str()); diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index 154180072..a33763596 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -181,6 +181,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -221,6 +222,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -285,6 +287,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) else if (this->currentmode == NavigationStyle::DRAGGING) { this->addToLog(event->getPosition(), event->getTime()); this->spin(posn); + moveCursorPosition(); processed = TRUE; } } @@ -338,6 +341,9 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) newmode = NavigationStyle::PANNING; break; case BUTTON3DOWN: + if (newmode != NavigationStyle::DRAGGING) { + saveCursorPosition(ev); + } newmode = NavigationStyle::DRAGGING; break; case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN: diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index a85124a02..b6e49d632 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -210,6 +210,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -250,6 +251,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -314,6 +316,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) else if (this->currentmode == NavigationStyle::DRAGGING) { this->addToLog(event->getPosition(), event->getTime()); this->spin(posn); + moveCursorPosition(); processed = TRUE; } } @@ -379,6 +382,9 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) newmode = NavigationStyle::PANNING; break; case SHIFTDOWN|BUTTON2DOWN: + if (newmode != NavigationStyle::DRAGGING) { + saveCursorPosition(ev); + } newmode = NavigationStyle::DRAGGING; break; case CTRLDOWN|SHIFTDOWN|BUTTON2DOWN: diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index b7788cfcd..b4bf7c5a5 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -3,6 +3,9 @@ if(WIN32) add_definitions(-DFCGui -DQIIS_MAKEDLL) endif(WIN32) +if (FREECAD_USE_3DCONNEXION) +add_definitions(-D_USE_3DCONNEXION_SDK) +endif(FREECAD_USE_3DCONNEXION) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} @@ -78,6 +81,17 @@ SET(FreeCADGui_XML_SRCS ) SOURCE_GROUP("XML" FILES ${FreeCADApp_XML_SRCS}) +# The 3D Connexion SDK files +if(FREECAD_USE_3DCONNEXION) +SET(FreeCADGui_SDK_SRCS + 3Dconnexion/I3dMouseParams.h + 3Dconnexion/MouseParameters.cpp + 3Dconnexion/MouseParameters.h + 3Dconnexion/GuiApplicationNativeEventAwareWin32.cpp +) +SOURCE_GROUP("3D connexion SDK" FILES ${FreeCADGui_SDK_SRCS}) +endif(FREECAD_USE_3DCONNEXION) + set(Gui_MOC_HDRS Action.h CallTips.h @@ -769,6 +783,7 @@ SET(FreeCADGui_SRCS ) SET(FreeCADGui_SRCS + ${FreeCADGui_SDK_SRCS} ${FreeCADGui_CPP_SRCS} ${FreeCADGui_XML_SRCS} ${iis_MOC_SRCS} diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index 3d728e394..6d3b01a34 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -33,6 +33,7 @@ # include # include # include +# include #endif #include @@ -292,7 +293,7 @@ StdCmdMergeProjects::StdCmdMergeProjects() void StdCmdMergeProjects::activated(int iMsg) { - QString exe = QString::fromUtf8(App::GetApplication().getExecutableName()); + QString exe = qApp->applicationName(); QString project = QFileDialog::getOpenFileName(Gui::getMainWindow(), QString::fromUtf8(QT_TR_NOOP("Merge project")), QDir::homePath(), QString::fromUtf8(QT_TR_NOOP("%1 document (*.fcstd)")).arg(exe)); @@ -1176,16 +1177,27 @@ void StdCmdAlignment::activated(int iMsg) model.addGroups(groupMap); align->setModel(model); Base::Type style = Base::Type::fromName("Gui::CADNavigationStyle"); + Base::Vector3d upDir(0,1,0), viewDir(0,0,-1); Gui::Document* doc = Application::Instance->activeDocument(); if (doc) { View3DInventor* mdi = qobject_cast(doc->getActiveView()); if (mdi) { - style = mdi->getViewer()->navigationStyle()->getTypeId(); + View3DInventorViewer* viewer = mdi->getViewer(); + SoCamera* camera = viewer->getCamera(); + if (camera) { + SbVec3f up(0,1,0), dir(0,0,-1); + camera->orientation.getValue().multVec(dir, dir); + viewDir.Set(dir[0],dir[1],dir[2]); + camera->orientation.getValue().multVec(up, up); + upDir.Set(up[0],up[1],up[2]); + } + style = viewer->navigationStyle()->getTypeId(); } } align->setMinPoints(1); align->startAlignment(style); + align->setViewingDirections(viewDir,upDir, viewDir,upDir); Gui::Selection().clearSelection(); } diff --git a/src/Gui/CommandStd.cpp b/src/Gui/CommandStd.cpp index 3861e1e64..dba6d5f71 100644 --- a/src/Gui/CommandStd.cpp +++ b/src/Gui/CommandStd.cpp @@ -193,13 +193,7 @@ Action * StdCmdAbout::createAction(void) { Action *pcAction; - QString exe; - std::map& cfg = App::Application::Config(); - std::map::iterator it = cfg.find("Application"); - if (it != cfg.end()) - exe = QString::fromUtf8(it->second.c_str()); - else - exe = QString::fromUtf8(App::GetApplication().getExecutableName()); + QString exe = qApp->applicationName(); pcAction = new Action(this,getMainWindow()); pcAction->setText(QCoreApplication::translate( this->className(), sMenuText, 0, @@ -235,13 +229,7 @@ void StdCmdAbout::activated(int iMsg) void StdCmdAbout::languageChange() { if (_pcAction) { - QString exe; - std::map& cfg = App::Application::Config(); - std::map::iterator it = cfg.find("Application"); - if (it != cfg.end()) - exe = QString::fromUtf8(it->second.c_str()); - else - exe = QString::fromUtf8(App::GetApplication().getExecutableName()); + QString exe = qApp->applicationName(); _pcAction->setText(QCoreApplication::translate( this->className(), sMenuText, 0, QCoreApplication::CodecForTr).arg(exe)); diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 0192d3e86..972d9d148 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -405,6 +405,9 @@ void Document::slotDeletedObject(const App::DocumentObject& Obj) // cycling to all views of the document ViewProvider* viewProvider = getViewProvider(&Obj); +#if 0 // With this we can show child objects again if this method was called by undo + viewProvider->onDelete(std::vector()); +#endif if (viewProvider && viewProvider->getTypeId().isDerivedFrom (ViewProviderDocumentObject::getClassTypeId())) { // go through the views @@ -504,7 +507,7 @@ bool Document::saveAs(void) { getMainWindow()->statusBar()->showMessage(QObject::tr("Save document under new filename...")); - QString exe = QString::fromUtf8(App::GetApplication().getExecutableName()); + QString exe = qApp->applicationName(); QString fn = QFileDialog::getSaveFileName(getMainWindow(), QObject::tr("Save %1 Document").arg(exe), FileDialog::getWorkingDirectory(), QObject::tr("%1 document (*.FCStd)").arg(exe)); if (!fn.isEmpty()) { @@ -927,13 +930,16 @@ bool Document::canClose () return false; } else if (!Gui::Control().isAllowedAlterDocument()) { - QMessageBox::warning(getActiveView(), - QObject::tr("Document not closable"), - QObject::tr("The document is in editing mode and thus cannot be closed for the moment.\n" - "You either have to finish or cancel the editing in the task panel.")); - Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); - if (dlg) Gui::Control().showDialog(dlg); - return false; + std::string name = Gui::Control().activeDialog()->getDocumentName(); + if (name == this->getDocument()->getName()) { + QMessageBox::warning(getActiveView(), + QObject::tr("Document not closable"), + QObject::tr("The document is in editing mode and thus cannot be closed for the moment.\n" + "You either have to finish or cancel the editing in the task panel.")); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (dlg) Gui::Control().showDialog(dlg); + return false; + } } if (!isModified()) diff --git a/src/Gui/FileDialog.cpp b/src/Gui/FileDialog.cpp index bbadc2d14..a34c62fea 100644 --- a/src/Gui/FileDialog.cpp +++ b/src/Gui/FileDialog.cpp @@ -15,8 +15,8 @@ * * * 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 * + * write to the Free Software Foundation, Inc., 51 Franklin Street, * + * Fifth Floor, Boston, MA 02110-1301, USA * * * ***************************************************************************/ diff --git a/src/Gui/GuiApplicationNativeEventAware.cpp b/src/Gui/GuiApplicationNativeEventAware.cpp index e741fcfa4..f14beb3bb 100644 --- a/src/Gui/GuiApplicationNativeEventAware.cpp +++ b/src/Gui/GuiApplicationNativeEventAware.cpp @@ -40,11 +40,10 @@ #endif #endif -#ifdef Q_WS_WIN -#include +#ifdef _USE_3DCONNEXION_SDK +Gui::GUIApplicationNativeEventAware* Gui::GUIApplicationNativeEventAware::gMouseInput = 0; #endif - Gui::GUIApplicationNativeEventAware::GUIApplicationNativeEventAware(int &argc, char *argv[]) : QApplication (argc, argv), spaceballPresent(false) { @@ -59,11 +58,18 @@ Gui::GUIApplicationNativeEventAware::~GUIApplicationNativeEventAware() else Base::Console().Log("Disconnected from spacenav daemon\n"); #endif + +#ifdef _USE_3DCONNEXION_SDK + if (gMouseInput == this) { + gMouseInput = 0; + } +#endif } void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window) { mainWindow = window; + #ifdef SPNAV_FOUND if (spnav_x11_open(QX11Info::display(), window->winId()) == -1) Base::Console().Log("Couldn't connect to spacenav daemon\n"); @@ -74,6 +80,19 @@ void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window) } #endif +#ifdef _USE_3DCONNEXION_SDK + spaceballPresent = Is3dmouseAttached(); + + if (spaceballPresent) { + fLast3dmouseInputTime = 0; + + if (InitializeRawInput(mainWindow->winId())){ + gMouseInput = this; + qApp->setEventFilter(Gui::GUIApplicationNativeEventAware::RawInputEventFilter); + } + } +#endif // _USE_3DCONNEXION_SDK + Spaceball::MotionEvent::MotionEventType = QEvent::registerEventType(); Spaceball::ButtonEvent::ButtonEventType = QEvent::registerEventType(); } @@ -146,15 +165,8 @@ bool Gui::GUIApplicationNativeEventAware::x11EventFilter(XEvent *event) return true; #else return false; -#endif +#endif // SPNAV_FOUND } -#endif - -#ifdef Q_WS_WIN -bool Gui::GUIApplicationNativeEventAware::winEventFilter(MSG *msg, long *result) -{ - return false; -} -#endif +#endif // Q_WS_X11 #include "moc_GuiApplicationNativeEventAware.cpp" diff --git a/src/Gui/GuiApplicationNativeEventAware.h b/src/Gui/GuiApplicationNativeEventAware.h index b43245a65..113dd4b1f 100644 --- a/src/Gui/GuiApplicationNativeEventAware.h +++ b/src/Gui/GuiApplicationNativeEventAware.h @@ -29,6 +29,19 @@ class QMainWindow; + +#ifdef _USE_3DCONNEXION_SDK +#include "3Dconnexion/MouseParameters.h" + +#include +#include + +// #define _WIN32_WINNT 0x0501 //target at least windows XP + +#include + +#endif // _USE_3DCONNEXION_SDK + namespace Gui { class GUIApplicationNativeEventAware : public QApplication @@ -38,18 +51,65 @@ namespace Gui GUIApplicationNativeEventAware(int &argc, char *argv[]); ~GUIApplicationNativeEventAware(); void initSpaceball(QMainWindow *window); + bool isSpaceballPresent() const {return spaceballPresent;} bool processSpaceballEvent(QObject *object, QEvent *event); -#ifdef Q_WS_X11 - bool x11EventFilter(XEvent *event); -#endif - -#ifdef Q_WS_WIN - bool winEventFilter(MSG *msg, long *result); -#endif - bool isSpaceballPresent(){return spaceballPresent;} private: bool spaceballPresent; QMainWindow *mainWindow; + +// For X11 +#ifdef Q_WS_X11 + public: + bool x11EventFilter(XEvent *event); +#endif // Q_WS_X11 + +// For Windows +#ifdef _USE_3DCONNEXION_SDK + public: + static bool Is3dmouseAttached(); + + I3dMouseParam& MouseParams(); + const I3dMouseParam& MouseParams() const; + + virtual void Move3d(HANDLE device, std::vector& motionData); + virtual void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); + virtual void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); + + private: + bool InitializeRawInput(HWND hwndTarget); + static bool RawInputEventFilter(void* msg, long* result); + void OnRawInput(UINT nInputCode, HRAWINPUT hRawInput); + UINT GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader); + bool TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput); + void On3dmouseInput(); + + class TInputData + { + public: + TInputData() : fAxes(6) {} + + bool IsZero() { + return (0.==fAxes[0] && 0.==fAxes[1] && 0.==fAxes[2] && + 0.==fAxes[3] && 0.==fAxes[4] && 0.==fAxes[5]); + } + + int fTimeToLive; // For telling if the device was unplugged while sending data + bool fIsDirty; + std::vector fAxes; + }; + + HWND fWindow; + + // Data cache to handle multiple rawinput devices + std::map< HANDLE, TInputData> fDevice2Data; + std::map< HANDLE, unsigned long> fDevice2Keystate; + // 3dmouse parameters + MouseParameters f3dMouseParams; // Rotate, Pan Zoom etc. + // use to calculate distance traveled since last event + DWORD fLast3dmouseInputTime; + static Gui::GUIApplicationNativeEventAware* gMouseInput; +#endif // _USE_3DCONNEXION_SDK }; } + #endif // GUIAPPLICATIONNATIVEEVENTAWARE_H diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index e1e6f4755..2ebc53371 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -279,6 +279,7 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) else if (this->currentmode == NavigationStyle::DRAGGING) { this->addToLog(event->getPosition(), event->getTime()); this->spin(posn); + moveCursorPosition(); processed = TRUE; } } @@ -316,6 +317,9 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) } break; case BUTTON1DOWN: + if (newmode != NavigationStyle::DRAGGING) { + saveCursorPosition(ev); + } newmode = NavigationStyle::DRAGGING; break; case BUTTON3DOWN: diff --git a/src/Gui/Macro.cpp b/src/Gui/Macro.cpp index 89f9fab1a..1ac796478 100644 --- a/src/Gui/Macro.cpp +++ b/src/Gui/Macro.cpp @@ -26,6 +26,7 @@ #ifndef _PreComp_ # include # include +# include # include # include #endif @@ -232,9 +233,7 @@ void MacroManager::run(MacroType eType,const char *sName) Base::Interpreter().runFile(sName, this->localEnv); } catch (const Base::SystemExitException&) { - Base::PyGILStateLocker lock; - PyErr_Clear(); - Base::Interpreter().systemExit(); + qApp->quit(); } catch (const Base::PyException& e) { Base::Console().Error("%s%s: %s\n", diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 8c129d761..a13a16c2c 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -1152,7 +1153,13 @@ void MainWindow::showMainWindow() void MainWindow::delayedStartup() { // processing all command line files - App::Application::processCmdLineFiles(); + try { + App::Application::processCmdLineFiles(); + } + catch (const Base::SystemExitException&) { + QApplication::quit(); + return; + } const std::map& cfg = App::Application::Config(); std::map::const_iterator it = cfg.find("StartHidden"); @@ -1318,10 +1325,7 @@ QPixmap MainWindow::splashImage() const // include application name and version number std::map::const_iterator tc = App::Application::Config().find("SplashInfoColor"); if (tc != App::Application::Config().end()) { - QString title = QString::fromAscii(App::Application::Config()["ExeName"].c_str()); - std::map::iterator it = App::Application::Config().find("Application"); - if (it != App::Application::Config().end()) - title = QString::fromUtf8(it->second.c_str()); + QString title = qApp->applicationName(); QString major = QString::fromAscii(App::Application::Config()["BuildVersionMajor"].c_str()); QString minor = QString::fromAscii(App::Application::Config()["BuildVersionMinor"].c_str()); QString version = QString::fromAscii("%1.%2").arg(major).arg(minor); diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index 50dba3cf2..85cada0b8 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -54,10 +54,14 @@ struct NavigationStyleP { SbVec3f focal1, focal2; SbRotation endRotation; SoTimerSensor * animsensor; + float sensitivity; + SbBool resetcursorpos; NavigationStyleP() { this->animationsteps = 0; + this->sensitivity = 2.0f; + this->resetcursorpos = FALSE; } static void viewAnimationCB(void * data, SoSensor * sensor); }; @@ -372,7 +376,7 @@ void NavigationStyle::lookAtPoint(const SbVec3f& pos) } } -void NavigationStyle::setCameraOrientation(const SbRotation& rot) +void NavigationStyle::setCameraOrientation(const SbRotation& rot, SbBool moveToCenter) { SoCamera* cam = viewer->getCamera(); if (cam == 0) return; @@ -383,16 +387,18 @@ void NavigationStyle::setCameraOrientation(const SbRotation& rot) PRIVATE(this)->focal1 = cam->position.getValue() + cam->focalDistance.getValue() * direction; PRIVATE(this)->focal2 = PRIVATE(this)->focal1; - SoGetBoundingBoxAction action(viewer->getViewportRegion()); - action.apply(viewer->getSceneGraph()); - SbBox3f box = action.getBoundingBox(); - if (!box.isEmpty()) { - rot.multVec(SbVec3f(0, 0, -1), direction); - //float s = (this->focal1 - box.getCenter()).dot(direction); - //this->focal2 = box.getCenter() + s * direction; - // setting the center of the overall bounding box as the future focal point - // seems to be a satisfactory solution - PRIVATE(this)->focal2 = box.getCenter(); + if (moveToCenter) { + SoGetBoundingBoxAction action(viewer->getViewportRegion()); + action.apply(viewer->getSceneGraph()); + SbBox3f box = action.getBoundingBox(); + if (!box.isEmpty()) { + rot.multVec(SbVec3f(0, 0, -1), direction); + //float s = (this->focal1 - box.getCenter()).dot(direction); + //this->focal2 = box.getCenter() + s * direction; + // setting the center of the overall bounding box as the future focal point + // seems to be a satisfactory solution + PRIVATE(this)->focal2 = box.getCenter(); + } } // avoid to interfere with spinning (fixes #3101462) @@ -780,6 +786,14 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) this->spinprojector->project(lastpos); SbRotation r; this->spinprojector->projectAndGetRotation(pointerpos, r); + float sensitivity = getSensitivity(); + if (sensitivity > 1.0f) { + SbVec3f axis; + float radians; + r.getValue(axis, radians); + radians = sensitivity * radians; + r.setValue(axis, radians); + } r.invert(); this->reorientCamera(viewer->getCamera(), r); @@ -834,6 +848,25 @@ SbBool NavigationStyle::doSpin() return FALSE; } +void NavigationStyle::saveCursorPosition(const SoEvent * const ev) +{ + this->globalPos.setValue(QCursor::pos().x(), QCursor::pos().y()); + this->localPos = ev->getPosition(); +} + +void NavigationStyle::moveCursorPosition() +{ + if (!isResetCursorPosition()) + return; + + QPoint cpos = QCursor::pos(); + if (abs(cpos.x()-globalPos[0]) > 10 || + abs(cpos.y()-globalPos[1]) > 10) { + QCursor::setPos(globalPos[0], globalPos[1]-1); + this->log.position[0] = localPos; + } +} + void NavigationStyle::updateAnimation() { SbTime now = SbTime::getTimeOfDay(); @@ -923,6 +956,26 @@ void NavigationStyle::stopAnimating(void) NavigationStyle::IDLE : NavigationStyle::INTERACT); } +void NavigationStyle::setSensitivity(float val) +{ + PRIVATE(this)->sensitivity = val; +} + +float NavigationStyle::getSensitivity() const +{ + return PRIVATE(this)->sensitivity; +} + +void NavigationStyle::setResetCursorPosition(SbBool on) +{ + PRIVATE(this)->resetcursorpos = on; +} + +SbBool NavigationStyle::isResetCursorPosition() const +{ + return PRIVATE(this)->resetcursorpos; +} + void NavigationStyle::setZoomInverted(SbBool on) { this->invertZoom = on; diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index cf7a1cdbc..2e4900173 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -110,6 +110,12 @@ public: void stopAnimating(void); SbBool isAnimating(void) const; + void setSensitivity(float); + float getSensitivity() const; + + void setResetCursorPosition(SbBool); + SbBool isResetCursorPosition() const; + void setZoomInverted(SbBool); SbBool isZoomInverted() const; void setZoomStep(float); @@ -121,7 +127,7 @@ public: void updateAnimation(); void redraw(); - void setCameraOrientation(const SbRotation& rot); + void setCameraOrientation(const SbRotation& rot, SbBool moveTocenter=false); void lookAtPoint(const SbVec3f&); void boxZoom(const SbBox2s& box); virtual void viewAll(); @@ -172,6 +178,8 @@ protected: void doZoom(SoCamera * camera, SbBool forward, const SbVec2f& pos); void spin(const SbVec2f & pointerpos); SbBool doSpin(); + void moveCursorPosition(); + void saveCursorPosition(const SoEvent * const ev); SbBool handleEventInForeground(const SoEvent* const e); virtual SbBool processSoEvent(const SoEvent * const ev); @@ -192,6 +200,8 @@ protected: View3DInventorViewer* viewer; ViewerMode currentmode; SbVec2f lastmouseposition; + SbVec2s globalPos; + SbVec2s localPos; SbPlane panningplane; SbTime prevRedrawTime; SbTime centerTime; diff --git a/src/Gui/PythonConsole.cpp b/src/Gui/PythonConsole.cpp index edccd488a..8364ae4e9 100644 --- a/src/Gui/PythonConsole.cpp +++ b/src/Gui/PythonConsole.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include @@ -700,12 +701,13 @@ void PythonConsole::runSource(const QString& line) catch (const Base::SystemExitException&) { ParameterGrp::handle hPrefGrp = getWindowParameter(); bool check = hPrefGrp->GetBool("CheckSystemExit",true); - if (!check) Base::Interpreter().systemExit(); + if (!check) qApp->quit(); int ret = QMessageBox::question(this, tr("System exit"), tr("The application is still running.\nDo you want to exit without saving your data?"), QMessageBox::Yes, QMessageBox::No|QMessageBox::Escape|QMessageBox::Default); if (ret == QMessageBox::Yes) { - Base::Interpreter().systemExit(); - } else { + qApp->quit(); + } + else { PyErr_Clear(); } } diff --git a/src/Gui/PythonEditor.cpp b/src/Gui/PythonEditor.cpp index 5b448633a..14db48db7 100644 --- a/src/Gui/PythonEditor.cpp +++ b/src/Gui/PythonEditor.cpp @@ -180,21 +180,21 @@ public: PythonSyntaxHighlighterP() { keywords << QLatin1String("and") << QLatin1String("as") - << QLatin1String("assert") - << QLatin1String("break") << QLatin1String("class") - << QLatin1String("continue") << QLatin1String("def") - << QLatin1String("del") << QLatin1String("elif") - << QLatin1String("else") << QLatin1String("except") - << QLatin1String("exec") << QLatin1String("finally") - << QLatin1String("for") << QLatin1String("from") - << QLatin1String("global") << QLatin1String("if") - << QLatin1String("import") << QLatin1String("in") - << QLatin1String("is") << QLatin1String("lambda") - << QLatin1String("None") << QLatin1String("not") - << QLatin1String("or") << QLatin1String("pass") - << QLatin1String("print") << QLatin1String("raise") - << QLatin1String("return") << QLatin1String("try") - << QLatin1String("while") << QLatin1String("yield"); + << QLatin1String("assert") << QLatin1String("break") + << QLatin1String("class") << QLatin1String("continue") + << QLatin1String("def") << QLatin1String("del") + << QLatin1String("elif") << QLatin1String("else") + << QLatin1String("except") << QLatin1String("exec") + << QLatin1String("finally") << QLatin1String("for") + << QLatin1String("from") << QLatin1String("global") + << QLatin1String("if") << QLatin1String("import") + << QLatin1String("in") << QLatin1String("is") + << QLatin1String("lambda") << QLatin1String("None") + << QLatin1String("not") << QLatin1String("or") + << QLatin1String("pass") << QLatin1String("print") + << QLatin1String("raise") << QLatin1String("return") + << QLatin1String("try") << QLatin1String("while") + << QLatin1String("with") << QLatin1String("yield"); } QStringList keywords; diff --git a/src/Gui/Splashscreen.cpp b/src/Gui/Splashscreen.cpp index 9464a5da1..46f1183e9 100644 --- a/src/Gui/Splashscreen.cpp +++ b/src/Gui/Splashscreen.cpp @@ -26,6 +26,7 @@ # include # include # include +# include # include # include # include @@ -88,23 +89,31 @@ public: textColor = col; } } - virtual ~SplashObserver() { Base::Console().DetachObserver(this); } - + const char* Name() + { + return "SplashObserver"; + } void Warning(const char * s) { +#ifdef FC_DEBUG Log(s); +#endif } void Message(const char * s) { +#ifdef FC_DEBUG Log(s); +#endif } void Error (const char * s) { +#ifdef FC_DEBUG Log(s); +#endif } void Log (const char * s) { @@ -174,7 +183,11 @@ AboutDialogFactory::~AboutDialogFactory() QDialog *AboutDialogFactory::create(QWidget *parent) const { +#ifdef _USE_3DCONNEXION_SDK + return new AboutDialog(true, parent); +#else return new AboutDialog(false, parent); +#endif } const AboutDialogFactory *AboutDialogFactory::defaultFactory() @@ -267,11 +280,9 @@ static QString getPlatform() void AboutDialog::setupLabels() { + QString exeName = qApp->applicationName(); std::map& config = App::Application::Config(); - QString exeName = QString::fromAscii(config["ExeName"].c_str()); - std::map::iterator it = config.find("WindowTitle"); - if (it != config.end()) - exeName = QString::fromUtf8(it->second.c_str()); + std::map::iterator it; QString banner = QString::fromUtf8(config["CopyrightInfo"].c_str()); banner = banner.left( banner.indexOf(QLatin1Char('\n')) ); QString major = QString::fromAscii(config["BuildVersionMajor"].c_str()); @@ -328,8 +339,54 @@ void AboutDialog::setupLabels() } } +namespace Gui { +namespace Dialog { + +class GuiExport LicenseDialog : public QDialog +{ +public: + LicenseDialog(QWidget *parent = 0) : QDialog(parent, Qt::FramelessWindowHint) + { + QString info; +#ifdef _USE_3DCONNEXION_SDK + info = QString::fromAscii( + "3D Mouse Support:\n" + "Development tools and related technology provided under license from 3Dconnexion.\n" + "(c) 1992 - 2012 3Dconnexion. All rights reserved"); +#endif + statusLabel = new QLabel(info); + buttonBox = new QDialogButtonBox; + buttonBox->setStandardButtons(QDialogButtonBox::Ok); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + + QHBoxLayout *topLayout = new QHBoxLayout; + topLayout->addWidget(statusLabel); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(topLayout); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Copyright")); + } + ~LicenseDialog() + { + } + +private: + QLabel *statusLabel; + QDialogButtonBox *buttonBox; +}; + +} // namespace Dialog +} // namespace Gui + void AboutDialog::on_licenseButton_clicked() { +#ifdef _USE_3DCONNEXION_SDK + LicenseDialog dlg(this); + dlg.exec(); +#endif } void AboutDialog::on_copyButton_clicked() diff --git a/src/Gui/TaskView/TaskDialog.h b/src/Gui/TaskView/TaskDialog.h index 30dc84ddc..f6a0e402f 100644 --- a/src/Gui/TaskView/TaskDialog.h +++ b/src/Gui/TaskView/TaskDialog.h @@ -67,6 +67,8 @@ public: virtual void modifyStandardButtons(QDialogButtonBox*) {} + const std::string& getDocumentName() const + { return documentName; } virtual bool isAllowedAlterDocument(void) const { return false; } virtual bool isAllowedAlterView(void) const @@ -92,6 +94,7 @@ protected: /// List of TaskBoxes of that dialog std::vector Content; ButtonPosition pos; + std::string documentName; }; } //namespace TaskView diff --git a/src/Gui/Thumbnail.cpp b/src/Gui/Thumbnail.cpp index e6f362ad0..df5cbdf3a 100644 --- a/src/Gui/Thumbnail.cpp +++ b/src/Gui/Thumbnail.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include @@ -97,7 +98,7 @@ void Thumbnail::SaveDocFile (Base::Writer &writer) const // according to specification add some meta-information to the image uint mt = QDateTime::currentDateTime().toTime_t(); QString mtime = QString::fromAscii("%1").arg(mt); - img.setText(QLatin1String("Software"), QString::fromUtf8(App::GetApplication().getExecutableName())); + img.setText(QLatin1String("Software"), qApp->applicationName()); img.setText(QLatin1String("Thumb::Mimetype"), QLatin1String("application/x-extension-fcstd")); img.setText(QLatin1String("Thumb::MTime"), mtime); img.setText(QLatin1String("Thumb::URI"), this->uri.toString()); diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index e33323c33..9eca5aafc 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -185,6 +185,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) else if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -225,6 +226,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) if (press && (this->currentmode == NavigationStyle::PANNING || this->currentmode == NavigationStyle::ZOOMING)) { newmode = NavigationStyle::DRAGGING; + saveCursorPosition(ev); this->centerTime = ev->getTime(); processed = TRUE; } @@ -259,6 +261,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) else if (this->currentmode == NavigationStyle::DRAGGING) { this->addToLog(event->getPosition(), event->getTime()); this->spin(posn); + moveCursorPosition(); processed = TRUE; } } @@ -305,6 +308,9 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) break; case ALTDOWN: case CTRLDOWN|SHIFTDOWN: + if (newmode != NavigationStyle::DRAGGING) { + saveCursorPosition(ev); + } newmode = NavigationStyle::DRAGGING; break; case CTRLDOWN|SHIFTDOWN|BUTTON1DOWN: diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index e1f035a4a..735ab91bc 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -140,6 +140,8 @@ View3DInventor::View3DInventor(Gui::Document* pcDocument, QWidget* parent, Qt::W OnChange(*hGrp,"BacklightIntensity"); OnChange(*hGrp,"NavigationStyle"); OnChange(*hGrp,"OrbitStyle"); + OnChange(*hGrp,"Sensitivity"); + OnChange(*hGrp,"ResetCursorPosition"); stopSpinTimer = new QTimer(this); connect(stopSpinTimer, SIGNAL(timeout()), this, SLOT(stopAnimating())); @@ -274,6 +276,14 @@ void View3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::M int style = rGrp.GetInt("OrbitStyle",1); _viewer->navigationStyle()->setOrbitStyle(NavigationStyle::OrbitStyle(style)); } + else if (strcmp(Reason,"Sensitivity") == 0) { + float val = rGrp.GetFloat("Sensitivity",2.0f); + _viewer->navigationStyle()->setSensitivity(val); + } + else if (strcmp(Reason,"ResetCursorPosition") == 0) { + bool on = rGrp.GetBool("ResetCursorPosition",false); + _viewer->navigationStyle()->setResetCursorPosition(on); + } else if (strcmp(Reason,"InvertZoom") == 0) { bool on = rGrp.GetBool("InvertZoom", false); _viewer->navigationStyle()->setZoomInverted(on); diff --git a/src/Gui/View3DInventorExamples.cpp b/src/Gui/View3DInventorExamples.cpp index 99510a005..c7e5a1b4f 100644 --- a/src/Gui/View3DInventorExamples.cpp +++ b/src/Gui/View3DInventorExamples.cpp @@ -311,7 +311,7 @@ void LightManip(SoSeparator * root) in.setBuffer((void *)scenegraph, std::strlen(scenegraph)); SoSeparator * _root = SoDB::readAll( &in ); root->addChild(_root); - if ( root == NULL ) exit( 1 ); // Shouldn't happen. + if ( root == NULL ) return; // Shouldn't happen. root->ref(); const char * pointlightnames[3] = { "RedLight", "GreenLight", "BlueLight" }; @@ -323,7 +323,7 @@ void LightManip(SoSeparator * root) sa.setSearchingAll( FALSE ); sa.apply( root ); SoPath * path = sa.getPath(); - if ( path == NULL) exit( 1 ); // Shouldn't happen. + if ( path == NULL) return; // Shouldn't happen. SoPointLightManip * manip = new SoPointLightManip; manip->replaceNode( path ); @@ -446,7 +446,7 @@ void AnimationTexture(SoSeparator * root) texturetimer->schedule(); // Scene graph - if ( root == NULL ) exit( 1 ); // Shouldn't happen. + if ( root == NULL ) return; // Shouldn't happen. root->ref(); // prevent from being deleted because of the still running timer sensor // SoSeparator * root = new SoSeparator; // root->ref(); diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 93d06c691..7c61cfc01 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -1309,9 +1309,9 @@ void View3DInventorViewer::pubSeekToPoint(const SbVec3f& pos) this->seekToPoint(pos); } -void View3DInventorViewer::setCameraOrientation(const SbRotation& rot) +void View3DInventorViewer::setCameraOrientation(const SbRotation& rot, SbBool moveTocenter) { - navigation->setCameraOrientation(rot); + navigation->setCameraOrientation(rot, moveTocenter); } void View3DInventorViewer::setCameraType(SoType t) diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 71e5a2bf3..a7b1593c1 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -231,7 +231,7 @@ public: * \a TRUE the reorientation is animated, otherwise its directly * set. */ - void setCameraOrientation(const SbRotation& rot); + void setCameraOrientation(const SbRotation& rot, SbBool moveTocenter=false); void setCameraType(SoType t); void moveCameraTo(const SbRotation& rot, const SbVec3f& pos, int steps, int ms); /** diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index 14ab2e623..1fd6e034e 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -464,7 +464,8 @@ Py::Object View3DInventorPy::viewRotateRight(const Py::Tuple& args) Py::Object View3DInventorPy::setCameraOrientation(const Py::Tuple& args) { PyObject* o; - if (!PyArg_ParseTuple(args.ptr(), "O!", &PyTuple_Type, &o)) + PyObject* m=0; + if (!PyArg_ParseTuple(args.ptr(), "O!|O!", &PyTuple_Type, &o, &PyBool_Type, &m)) throw Py::Exception(); try { @@ -473,7 +474,7 @@ Py::Object View3DInventorPy::setCameraOrientation(const Py::Tuple& args) float q1 = (float)Py::Float(tuple[1]); float q2 = (float)Py::Float(tuple[2]); float q3 = (float)Py::Float(tuple[3]); - _view->getViewer()->setCameraOrientation(SbRotation(q0, q1, q2, q3)); + _view->getViewer()->setCameraOrientation(SbRotation(q0, q1, q2, q3), m==Py_True); } catch (const Base::Exception& e) { throw Py::Exception(e.what()); diff --git a/src/Main/MainCmd.cpp b/src/Main/MainCmd.cpp index f42d7598b..4fcae2687 100644 --- a/src/Main/MainCmd.cpp +++ b/src/Main/MainCmd.cpp @@ -37,6 +37,7 @@ #include #include +#include // FreeCAD Base header #include @@ -89,6 +90,14 @@ int main( int argc, char ** argv ) // Inits the Application App::Application::init(argc,argv); } + catch (const Base::UnknownProgramOption& e) { + std::cerr << e.what(); + exit(1); + } + catch (const Base::ProgramInformation& e) { + std::cout << e.what(); + exit(0); + } catch (const Base::Exception& e) { std::string appName = App::Application::Config()["ExeName"]; std::stringstream msg; diff --git a/src/Main/MainGui.cpp b/src/Main/MainGui.cpp index 83c4487f4..5552bd6b4 100644 --- a/src/Main/MainGui.cpp +++ b/src/Main/MainGui.cpp @@ -161,83 +161,6 @@ private: QDomDocument domDocument; }; -class ProgramOptions -{ -public: - ProgramOptions() - { - newcout = new ProgramOptionsStream(out); - oldcout = std::cout.rdbuf(newcout); - out.reserve(80); - newcerr = new ProgramOptionsStream(err); - oldcerr = std::cerr.rdbuf(newcerr); - err.reserve(80); - - error = true; - ::atexit(ProgramOptions::failure); - } - ~ProgramOptions() - { - std::cout.rdbuf(oldcout); - delete newcout; - std::cerr.rdbuf(oldcerr); - delete newcerr; - error = false; - } - static void failure() - { - if (error) { - int argc=0; - QApplication app(argc,0); - QString appName = QString::fromAscii(App::Application::Config()["ExeName"].c_str()); - if (!err.empty()) { - QString msg = QString::fromAscii(err.c_str()); - QString s = QLatin1String("
") + msg + QLatin1String("
"); - QMessageBox::critical(0, appName, s); - } - else if (!out.empty()) { - QString msg = QString::fromAscii(out.c_str()); - QString s = QLatin1String("
") + msg + QLatin1String("
"); - QMessageBox::information(0, appName, s); - } - } - } - -private: - class ProgramOptionsStream : public std::streambuf - { - public: - ProgramOptionsStream(std::string& s) : buf(s) - { - } - int overflow(int c = EOF) - { - if (c != EOF) - buf.push_back((char)c); - return c; - } - int sync() - { - return 0; - } - private: - std::string& buf; - }; - -private: - friend class ProgramOptionsStream; - std::streambuf* oldcout; - std::streambuf* newcout; - std::streambuf* oldcerr; - std::streambuf* newcerr; - static std::string out, err; - static bool error; -}; - -bool ProgramOptions::error = false; -std::string ProgramOptions::out; -std::string ProgramOptions::err; - #if defined (FC_OS_LINUX) || defined(FC_OS_BSD) QString myDecoderFunc(const QByteArray &localFileName) { @@ -300,10 +223,25 @@ int main( int argc, char ** argv ) App::Application::Config()["RunMode"] = "Gui"; // Inits the Application - ProgramOptions po; App::Application::init(argc,argv); Gui::Application::initApplication(); } + catch (const Base::UnknownProgramOption& e) { + QApplication app(argc,argv); + QString appName = QString::fromAscii(App::Application::Config()["ExeName"].c_str()); + QString msg = QString::fromAscii(e.what()); + QString s = QLatin1String("
") + msg + QLatin1String("
"); + QMessageBox::critical(0, appName, s); + exit(1); + } + catch (const Base::ProgramInformation& e) { + QApplication app(argc,argv); + QString appName = QString::fromAscii(App::Application::Config()["ExeName"].c_str()); + QString msg = QString::fromAscii(e.what()); + QString s = QLatin1String("
") + msg + QLatin1String("
"); + QMessageBox::information(0, appName, s); + exit(0); + } catch (const Base::Exception& e) { // Popup an own dialog box instead of that one of Windows QApplication app(argc,argv); diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 2025a6f30..66babfc7c 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -75,6 +75,14 @@ def removeComponents(objectsList,host=None): objectsList = [objectsList] if host: if Draft.getType(host) in ["Wall","Structure"]: + if hasattr(host,"Axes"): + a = host.Axes + print a + for o in objectsList[:]: + print o.Name + if o in a: + a.remove(o) + objectsList.remove(o) s = host.Subtractions for o in objectsList: if not o in s: @@ -154,31 +162,36 @@ def splitMesh(obj,mark=True): def makeFace(wires,method=2,cleanup=False): '''makeFace(wires): makes a face from a list of wires, finding which ones are holes''' - + #print "makeFace: start:", wires import Part if not isinstance(wires,list): + if len(wires.Vertexes) < 3: + raise return Part.Face(wires) elif len(wires) == 1: + #import Draft;Draft.printShape(wires[0]) + if len(wires[0].Vertexes) < 3: + raise return Part.Face(wires[0]) wires = wires[:] - print "inner wires found" + #print "makeFace: inner wires found" ext = None max_length = 0 # cleaning up rubbish in wires if cleanup: for i in range(len(wires)): wires[i] = DraftGeomUtils.removeInterVertices(wires[i]) - print "garbage removed" + #print "makeFace: garbage removed" for w in wires: # we assume that the exterior boundary is that one with # the biggest bounding box if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength ext = w - print "exterior wire",ext + #print "makeFace: exterior wire",ext wires.remove(ext) if method == 1: @@ -186,23 +199,22 @@ def makeFace(wires,method=2,cleanup=False): # all interior wires mark a hole and must reverse # their orientation, otherwise Part.Face fails for w in wires: - print "reversing",w + #print "makeFace: reversing",w w.reverse() - print "reversed" # make sure that the exterior wires comes as first in the list wires.insert(0, ext) - print "done sorting", wires + #print "makeFace: done sorting", wires if wires: return Part.Face(wires) else: # method 2: use the cut method mf = Part.Face(ext) - print "external face:",mf + #print "makeFace: external face:",mf for w in wires: f = Part.Face(w) - print "internal face:",f + #print "makeFace: internal face:",f mf = mf.cut(f) - print "final face:",mf.Faces + #print "makeFace: final face:",mf.Faces return mf.Faces[0] def meshToShape(obj,mark=True): diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index 5e0206fe9..b97d022ed 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -62,7 +62,8 @@ def addToComponent(compobject,addobject,mod=None): l = getattr(compobject,mod) l.append(addobject) setattr(compobject,mod,l) - addobject.ViewObject.hide() + if mod != "Objects": + addobject.ViewObject.hide() else: for a in attribs[:3]: if hasattr(compobject,a): @@ -79,7 +80,7 @@ def removeFromComponent(compobject,subobject): it is added as a subtraction.''' if compobject == subobject: return found = False - attribs = ["Additions","Subtractions","Objects","Components","Base"] + attribs = ["Additions","Subtractions","Objects","Components","Base","Axes"] for a in attribs: if hasattr(compobject,a): if a == "Base": diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index 460d3a7d1..f71828a96 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -21,7 +21,7 @@ #* * #*************************************************************************** -import FreeCAD,FreeCADGui,ArchComponent,WorkingPlane,math,Draft,ArchCommands, DraftVecUtils +import FreeCAD,FreeCADGui,ArchComponent,WorkingPlane,math,Draft,ArchCommands,DraftVecUtils from FreeCAD import Vector from PyQt4 import QtCore from pivy import coin @@ -215,7 +215,7 @@ class _ArchDrawingView: import ArchVRM render = ArchVRM.Renderer() render.setWorkingPlane(obj.Source.Placement) - render.addObjects(objs) + render.addObjects(Draft.getGroupContents(objs,walls=True)) render.cut(obj.Source.Shape) svg += render.getViewSVG(linewidth=linewidth) svg += render.getSectionSVG(linewidth=linewidth*2) diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index e30c14106..b63853fe1 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -86,6 +86,8 @@ class _Structure(ArchComponent.Component): str(translate("Arch","Axes systems this structure is built on"))) obj.addProperty("App::PropertyVector","Normal","Base", str(translate("Arch","The normal extrusion direction of this object (keep (0,0,0) for automatic normal)"))) + obj.addProperty("App::PropertyIntegerList","Exclude","Base", + str(translate("Arch","The element numbers to exclude when this structure is based on axes"))) self.Type = "Structure" def execute(self,obj): @@ -110,6 +112,12 @@ class _Structure(ArchComponent.Component): pts.extend(DraftGeomUtils.findIntersection(e1,e2)) return pts + def getAxisPlacement(self,obj): + "returns an axis placement" + if obj.Axes: + return obj.Axes[0].Placement + return None + def createGeometry(self,obj): import Part, DraftGeomUtils @@ -174,22 +182,32 @@ class _Structure(ArchComponent.Component): if not hole.Shape.isNull(): base = base.cut(hole.Shape) hole.ViewObject.hide() # to be removed + + # applying axes pts = self.getAxisPoints(obj) + apl = self.getAxisPlacement(obj) if pts: fsh = [] - for p in pts: + for i in range(len(pts)): + if hasattr(obj,"Exclude"): + if i in obj.Exclude: + continue sh = base.copy() - sh.translate(p) + if apl: + sh.Placement.Rotation = apl.Rotation + sh.translate(pts[i]) fsh.append(sh) obj.Shape = Part.makeCompound(fsh) + + # finalizing else: if base: if not base.isNull(): base = base.removeSplitter() obj.Shape = base - if not DraftGeomUtils.isNull(pl): - obj.Placement = pl - + if not DraftGeomUtils.isNull(pl): + obj.Placement = pl + class _ViewProviderStructure(ArchComponent.ViewProviderComponent): "A View Provider for the Structure object" diff --git a/src/Mod/Arch/ArchVRM.py b/src/Mod/Arch/ArchVRM.py index 2eefd8f27..91edfed56 100644 --- a/src/Mod/Arch/ArchVRM.py +++ b/src/Mod/Arch/ArchVRM.py @@ -145,12 +145,14 @@ class Renderer: def reorient(self): "reorients the faces on the WP" + #print "VRM: start reorient" if not self.faces: return self.faces = [self.projectFace(f) for f in self.faces] if self.sections: self.sections = [self.projectFace(f) for f in self.sections] self.oriented = True + #print "VRM: end reorient" def removeHidden(self): "removes faces pointing outwards" @@ -166,17 +168,24 @@ class Renderer: def projectFace(self,face): "projects a single face on the WP" + #print "VRM: projectFace start: ",len(face[0].Vertexes)," verts, ",len(face[0].Edges)," edges" wires = [] + if not face[0].Wires: + if DEBUG: print "Error: Unable to project face on the WP" + return None norm = face[0].normalAt(0,0) for w in face[0].Wires: verts = [] edges = DraftGeomUtils.sortEdges(w.Edges) + #print len(edges)," edges after sorting" for e in edges: v = e.Vertexes[0].Point + #print v v = self.wp.getLocalCoords(v) verts.append(v) verts.append(verts[0]) if len(verts) > 2: + #print "new wire with ",len(verts) wires.append(Part.makePolygon(verts)) try: sh = ArchCommands.makeFace(wires) @@ -188,6 +197,7 @@ class Renderer: vnorm = self.wp.getLocalCoords(norm) if vnorm.getAngle(sh.normalAt(0,0)) > 1: sh.reverse() + #print "VRM: projectFace end: ",len(sh.Vertexes)," verts" return [sh]+face[1:] def flattenFace(self,face): @@ -211,6 +221,7 @@ class Renderer: def cut(self,cutplane): "Cuts through the shapes with a given cut plane and builds section faces" + if DEBUG: print "\n\n======> Starting cut\n\n" if self.iscut: return if not self.shapes: @@ -262,12 +273,10 @@ class Renderer: shapes.append([c]+sh[1:]) for f in c.Faces: faces.append([f]+sh[1:]) - sec = sol.section(cutface) - if sec.Edges: - wires = DraftGeomUtils.findWires(sec.Edges) - for w in wires: - sec = Part.Face(w) - sections.append([sec,fill]) + print "iscoplanar:",f.Vertexes[0].Point,f.normalAt(0,0),cutface.Vertexes[0].Point,cutface.normalAt(0,0) + if DraftGeomUtils.isCoplanar([f,cutface]): + print "COPLANAR" + sections.append([f,fill]) self.shapes = shapes self.faces = faces self.sections = sections @@ -277,6 +286,7 @@ class Renderer: self.trimmed = False self.sorted = False self.joined = False + if DEBUG: print "\n\n======> Finished cut\n\n" def isInside(self,vert,face): "Returns True if the vert is inside the face in Z projection" @@ -319,6 +329,15 @@ class Renderer: def compare(self,face1,face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" + #print face1,face2 + + if not face1: + if DEBUG: print "Warning, undefined face!" + return 31 + elif not face2: + if DEBUG: print "Warning, undefined face!" + return 32 + # theory from # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) @@ -456,14 +475,17 @@ class Renderer: def sort(self): "projects a shape on the WP" + if DEBUG: print "\n\n======> Starting sort\n\n" if len(self.faces) <= 1: return if not self.trimmed: self.removeHidden() + if DEBUG: print "Done hidden face removal" if len(self.faces) == 1: return if not self.oriented: self.reorient() + if DEBUG: print "Done reorientation" faces = self.faces[:] if DEBUG: print "sorting ",len(self.faces)," faces" sfaces = [] @@ -492,6 +514,7 @@ class Renderer: for f2 in faces[1:]: if DEBUG: print "comparing face",str(self.faces.index(f1))," with face",str(self.faces.index(f2)) r = self.compare(f1,f2) + print "comparison result:",r if r == 1: faces.remove(f2) sfaces.append(f2) @@ -506,6 +529,10 @@ class Renderer: sfaces.append(f2) notfoundstack = 0 break + elif r == 31: + faces.remove(f1) + elif r == 32: + faces.remove(f2) else: # nothing found, move the face to the end of the pile faces.remove(f1) @@ -518,6 +545,7 @@ class Renderer: if DEBUG: print "done Z sorting. ", len(sfaces), " faces retained, ", len(self.faces)-len(sfaces), " faces lost." self.faces = sfaces self.sorted = True + if DEBUG: print "\n\n======> Finished sort\n\n" def buildDummy(self): "Builds a dummy object with faces spaced on the Z axis, for visual check" @@ -544,19 +572,21 @@ class Renderer: def getPathData(self,w): "Returns a SVG path data string from a 2D wire" + def tostr(val): + return str(round(val,DraftVecUtils.precision())) edges = DraftGeomUtils.sortEdges(w.Edges) v = edges[0].Vertexes[0].Point - svg = 'M '+ str(v.x) +' '+ str(v.y) + ' ' + svg = 'M '+ tostr(v.x) +' '+ tostr(v.y) + ' ' for e in edges: if isinstance(e.Curve,Part.Line) or isinstance(e.Curve,Part.BSplineCurve): v = e.Vertexes[-1].Point - svg += 'L '+ str(v.x) +' '+ str(v.y) + ' ' + svg += 'L '+ tostr(v.x) +' '+ tostr(v.y) + ' ' elif isinstance(e.Curve,Part.Circle): r = e.Curve.Radius v = e.Vertexes[-1].Point - svg += 'A '+ str(r) + ' '+ str(r) +' 0 0 1 '+ str(v.x) +' ' - svg += str(v.y) + ' ' - svg += 'z ' + svg += 'A '+ tostr(r) + ' '+ tostr(r) +' 0 0 1 '+ tostr(v.x) +' ' + svg += tostr(v.y) + ' ' + svg += 'Z ' return svg def getViewSVG(self,linewidth=0.01): @@ -566,44 +596,47 @@ class Renderer: self.sort() svg = '' for f in self.faces: - fill = self.getFill(f[1]) - svg +=' a2: + a1,a2 = a2,a1 + + #print "creating sketch arc from ",cu, ", p1=",v1, " (",math.degrees(a1), "d) p2=",v2," (", math.degrees(a2),"d)" + p= Part.ArcOfCircle(cu,a1,a2) + return p + else: + return edge.Curve def mirror (point, edge): "finds mirror point relative to an edge" @@ -445,98 +451,91 @@ def isLine(bsp): return True def sortEdges(lEdges, aVertex=None): - "an alternative, more accurate version of Part.__sortEdges__" + "an alternative, more accurate version of Part.__sortEdges__" - #There is no reason to limit this to lines only because every non-closed edge always - #has exactly two vertices (wmayer) - #for e in lEdges: - # if not isinstance(e.Curve,Part.Line): - # print "Warning: sortedges cannot treat wired containing curves yet." - # return lEdges - - def isSameVertex(V1, V2): - ''' Test if vertexes have same coordinates with precision 10E(-precision)''' - if round(V1.X-V2.X,1)==0 and round(V1.Y-V2.Y,1)==0 and round(V1.Z-V2.Z,1)==0 : - return True - else : - return False - - def lookfor(aVertex, inEdges): - ''' Look for (aVertex, inEdges) returns count, the position of the instance - the position in the instance and the instance of the Edge''' - count = 0 - linstances = [] #lists the instances of aVertex - for i in range(len(inEdges)) : - for j in range(2) : - if isSameVertex(aVertex,inEdges[i].Vertexes[j-1]): - instance = inEdges[i] - count += 1 - linstances += [i,j-1,instance] - return [count]+linstances - - if (len(lEdges) < 2): - if aVertex == None: - return lEdges + #There is no reason to limit this to lines only because every non-closed edge always + #has exactly two vertices (wmayer) + #for e in lEdges: + # if not isinstance(e.Curve,Part.Line): + # print "Warning: sortedges cannot treat wired containing curves yet." + # return lEdges + + def lookfor(aVertex, inEdges): + ''' Look for (aVertex, inEdges) returns count, the position of the instance + the position in the instance and the instance of the Edge''' + count = 0 + linstances = [] #lists the instances of aVertex + for i in range(len(inEdges)) : + for j in range(2) : + if aVertex.Point == inEdges[i].Vertexes[j-1].Point: + instance = inEdges[i] + count += 1 + linstances += [i,j-1,instance] + return [count]+linstances + + if (len(lEdges) < 2): + if aVertex == None: + return lEdges + else: + result = lookfor(aVertex,lEdges) + if result[0] != 0: + if aVertex.Point == result[3].Vertexes[0].Point: + return lEdges else: - result = lookfor(aVertex,lEdges) - if result[0] != 0: - if isSameVertex(aVertex,result[3].Vertexes[0]): - return lEdges - else: - if isinstance(result[3].Curve,Part.Line): - return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()] - elif isinstance(result[3].Curve,Part.Circle): - mp = findMidpoint(result[3]) - return [Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()] - elif isinstance(result[3].Curve,Part.BSplineCurve): - if isLine(result[3].Curve): - return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()] - else: - return lEdges - else: - return lEdges - - olEdges = [] # ol stands for ordered list - if aVertex == None: - for i in range(len(lEdges)*2) : - if len(lEdges[i/2].Vertexes) > 1: - result = lookfor(lEdges[i/2].Vertexes[i%2],lEdges) - if result[0] == 1 : # Have we found an end ? - olEdges = sortEdges(lEdges, result[3].Vertexes[result[2]]) - return olEdges - # if the wire is closed there is no end so choose 1st Vertex - #print "closed wire, starting from ",lEdges[0].Vertexes[0].Point - return sortEdges(lEdges, lEdges[0].Vertexes[0]) - else : - #print "looking ",aVertex.Point - result = lookfor(aVertex,lEdges) - if result[0] != 0 : - del lEdges[result[1]] - next = sortEdges(lEdges, result[3].Vertexes[-((-result[2])^1)]) - #print "result ",result[3].Vertexes[0].Point," ",result[3].Vertexes[1].Point, " compared to ",aVertex.Point - if isSameVertex(aVertex,result[3].Vertexes[0]): - #print "keeping" - olEdges += [result[3]] + next + if isinstance(result[3].Curve,Part.Line): + return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()] + elif isinstance(result[3].Curve,Part.Circle): + mp = findMidpoint(result[3]) + return [Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()] + elif isinstance(result[3].Curve,Part.BSplineCurve): + if isLine(result[3].Curve): + return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()] else: - #print "inverting", result[3].Curve - if isinstance(result[3].Curve,Part.Line): - newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape() - olEdges += [newedge] + next - elif isinstance(result[3].Curve,Part.Circle): - mp = findMidpoint(result[3]) - newedge = Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape() - olEdges += [newedge] + next - elif isinstance(result[3].Curve,Part.BSplineCurve): - if isLine(result[3].Curve): - newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape() - olEdges += [newedge] + next - else: - olEdges += [result[3]] + next - else: - olEdges += [result[3]] + next - return olEdges - else : - return [] + return lEdges + else: + return lEdges + + olEdges = [] # ol stands for ordered list + if aVertex == None: + for i in range(len(lEdges)*2) : + if len(lEdges[i/2].Vertexes) > 1: + result = lookfor(lEdges[i/2].Vertexes[i%2],lEdges) + if result[0] == 1 : # Have we found an end ? + olEdges = sortEdges(lEdges, result[3].Vertexes[result[2]]) + return olEdges + # if the wire is closed there is no end so choose 1st Vertex + # print "closed wire, starting from ",lEdges[0].Vertexes[0].Point + return sortEdges(lEdges, lEdges[0].Vertexes[0]) + else : + #print "looking ",aVertex.Point + result = lookfor(aVertex,lEdges) + if result[0] != 0 : + del lEdges[result[1]] + next = sortEdges(lEdges, result[3].Vertexes[-((-result[2])^1)]) + #print "result ",result[3].Vertexes[0].Point," ",result[3].Vertexes[1].Point, " compared to ",aVertex.Point + if aVertex.Point == result[3].Vertexes[0].Point: + #print "keeping" + olEdges += [result[3]] + next + else: + #print "inverting", result[3].Curve + if isinstance(result[3].Curve,Part.Line): + newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape() + olEdges += [newedge] + next + elif isinstance(result[3].Curve,Part.Circle): + mp = findMidpoint(result[3]) + newedge = Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape() + olEdges += [newedge] + next + elif isinstance(result[3].Curve,Part.BSplineCurve): + if isLine(result[3].Curve): + newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape() + olEdges += [newedge] + next + else: + olEdges += [result[3]] + next + else: + olEdges += [result[3]] + next + return olEdges + else : + return [] def findWires(edgeslist): @@ -787,6 +786,31 @@ def getNormal(shape): if n.getAngle(vdir) < 0.78: n = DraftVecUtils.neg(n) return n +def getRotation(v1,v2=FreeCAD.Vector(0,0,1)): + '''Get the rotation Quaternion between 2 vectors''' + if (v1.dot(v2) > 0.999999) or (v1.dot(v2) < -0.999999): + # vectors are opposite + return None + axis = v1.cross(v2) + axis.normalize() + angle = math.degrees(math.sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + v1.dot(v2)) + return FreeCAD.Rotation(axis,angle) + +def calculatePlacement(shape): + '''calculatePlacement(shape): if the given shape is planar, this function + returns a placement located at the center of gravity of the shape, and oriented + towards the shape's normal. Otherwise, it returns a null placement.''' + if not isPlanar(shape): + return FreeCAD.Placement() + pos = shape.BoundBox.Center + norm = getNormal(shape) + pla = FreeCAD.Placement() + pla.Base = pos + r = getRotation(norm) + if r: + pla.Rotation = r + return pla + def offsetWire(wire,dvec,bind=False,occ=False): ''' offsetWire(wire,vector,[bind]): offsets the given wire along the @@ -982,11 +1006,13 @@ def isCoplanar(faces): "checks if all faces in the given list are coplanar" if len(faces) < 2: return True - base =faces[0].normalAt(.5,.5) + base =faces[0].normalAt(0,0) for i in range(1,len(faces)): - normal = faces[i].normalAt(.5,.5) - if (normal.getAngle(base) > .0001) and (normal.getAngle(base) < 3.1415): - return False + for v in faces[i].Vertexes: + chord = v.Point.sub(faces[0].Vertexes[0].Point) + dist = DraftVecUtils.project(chord,base) + if round(dist.Length,DraftVecUtils.precision()) > 0: + return False return True def isPlanar(shape): diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index 55a4d7a23..ddc66e528 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -288,11 +288,13 @@ class Snapper: origin = Vector(self.snapInfo['x'],self.snapInfo['y'],self.snapInfo['z']) winner = [Vector(0,0,0),None,Vector(0,0,0)] for snap in snaps: - # if snap[0] == None: print "debug: Snapper: 'i[0]' is 'None'" - delta = snap[0].sub(origin) - if delta.Length < shortest: - shortest = delta.Length - winner = snap + if snap[0] == None: + print "debug: Snapper: snap point = ",snap + else: + delta = snap[0].sub(origin) + if delta.Length < shortest: + shortest = delta.Length + winner = snap # see if we are out of the max radius, if any if self.radius: diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index 7d7024771..909c664f5 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -565,7 +565,8 @@ bool MeshInput::LoadOFF (std::istream &rstrIn) meshPoints.reserve(numPoints); meshFacets.reserve(numFaces); - for (int i=0; iReturns a reparametrized copy of this surface + + + + approximate(points, degMin, degMax, continuity, tol) + approximate(zPoints, degMin, degMax, continuity, tol, X0, dX, Y0, dY) + + Replaces this B-Spline surface by approximating a set of points. + continuity is an integer between 0 and 3 + + + - Replaces this B-Spline surface by interpolating a set of points. + + interpolate(points) + interpolate(zpoints, X0, dX, Y0, dY) + + Replaces this B-Spline surface by interpolating a set of points. + The resulting surface is of degree 3 and continuity C2. + Arguments: + a 2 dimensional array of vectors, that the surface passes through + or + a 2 dimensional array of floats with the z values, + the x starting point X0 (float), + the x increment dX (float), + the y starting point Y0 and increment dY + diff --git a/src/Mod/Part/App/BSplineSurfacePyImp.cpp b/src/Mod/Part/App/BSplineSurfacePyImp.cpp index f642662f4..5fd9c9773 100644 --- a/src/Mod/Part/App/BSplineSurfacePyImp.cpp +++ b/src/Mod/Part/App/BSplineSurfacePyImp.cpp @@ -33,6 +33,7 @@ # include # include # include +# include #endif #include @@ -43,6 +44,7 @@ #include "BSplineSurfacePy.h" #include "BSplineSurfacePy.cpp" + using namespace Part; // returns a string which represents the object e.g. when printed in python @@ -1244,13 +1246,21 @@ PyObject* BSplineSurfacePy::reparametrize(PyObject * args) } } -PyObject* BSplineSurfacePy::interpolate(PyObject *args) +PyObject* BSplineSurfacePy::approximate(PyObject *args) { PyObject* obj; - double tol3d = Precision::Approximation(); - PyObject* closed = Py_False; - PyObject* t1=0; PyObject* t2=0; - if (!PyArg_ParseTuple(args, "O!",&(PyList_Type), &obj)) + Standard_Integer degMin=0; + Standard_Integer degMax=0; + Standard_Integer continuity=0; + Standard_Real tol3d = Precision::Approximation(); + Standard_Real X0=0; + Standard_Real dX=0; + Standard_Real Y0=0; + Standard_Real dY=0; + + int len = PyTuple_GET_SIZE(args); + + if (!PyArg_ParseTuple(args, "O!iiid|dddd",&(PyList_Type), &obj, °Min, °Max, &continuity, &tol3d, &X0, &dX, &Y0, &dY)) return 0; try { Py::List list(obj); @@ -1258,6 +1268,8 @@ PyObject* BSplineSurfacePy::interpolate(PyObject *args) Py::List col(list.getItem(0)); Standard_Integer lv = col.size(); TColgp_Array2OfPnt interpolationPoints(1, lu, 1, lv); + TColStd_Array2OfReal zPoints(1, lu, 1, lv); + //Base::Console().Message("lu=%d, lv=%d\n", lu, lv); Standard_Integer index1 = 0; Standard_Integer index2 = 0; @@ -1267,10 +1279,97 @@ PyObject* BSplineSurfacePy::interpolate(PyObject *args) Py::List row(*it1); for (Py::List::iterator it2 = row.begin(); it2 != row.end(); ++it2) { index2++; - Py::Vector v(*it2); - Base::Vector3d pnt = v.toVector(); - gp_Pnt newPoint(pnt.x,pnt.y,pnt.z); - interpolationPoints.SetValue(index1, index2, newPoint); + if(len == 5){ + Py::Vector v(*it2); + Base::Vector3d pnt = v.toVector(); + gp_Pnt newPoint(pnt.x,pnt.y,pnt.z); + interpolationPoints.SetValue(index1, index2, newPoint); + } + else { + Standard_Real val = PyFloat_AsDouble((*it2).ptr()); + zPoints.SetValue(index1, index2, val); + } + } + } + + if(continuity<0 || continuity>3){ + Standard_Failure::Raise("continuity must be between 0 and 3"); + } + GeomAbs_Shape c; + switch(continuity){ + case 0: + c = GeomAbs_C0; + case 1: + c = GeomAbs_C1; + case 2: + c = GeomAbs_C2; + case 3: + c = GeomAbs_C3; + } + + if (interpolationPoints.RowLength() < 2 || interpolationPoints.ColLength() < 2) { + Standard_Failure::Raise("not enough points given"); + } + + GeomAPI_PointsToBSplineSurface surInterpolation; + if(len == 5){ + surInterpolation.Init(interpolationPoints, degMin, degMax, c, tol3d); + } + else { + surInterpolation.Init(zPoints, X0, dX, Y0, dY, degMin, degMax, c, tol3d); + } + Handle_Geom_BSplineSurface sur(surInterpolation.Surface()); + this->getGeomBSplineSurfacePtr()->setHandle(sur); + Py_Return; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + std::string err = e->GetMessageString(); + if (err.empty()) err = e->DynamicType()->Name(); + PyErr_SetString(PyExc_Exception, err.c_str()); + return 0; + } +} + +PyObject* BSplineSurfacePy::interpolate(PyObject *args) +{ + PyObject* obj; + Standard_Real tol3d = Precision::Approximation(); + Standard_Real X0=0; + Standard_Real dX=0; + Standard_Real Y0=0; + Standard_Real dY=0; + + int len = PyTuple_GET_SIZE(args); + + if (!PyArg_ParseTuple(args, "O!|dddd",&(PyList_Type), &obj, &X0, &dX, &Y0, &dY)) + return 0; + try { + Py::List list(obj); + Standard_Integer lu = list.size(); + Py::List col(list.getItem(0)); + Standard_Integer lv = col.size(); + TColgp_Array2OfPnt interpolationPoints(1, lu, 1, lv); + TColStd_Array2OfReal zPoints(1, lu, 1, lv); + + Standard_Integer index1 = 0; + Standard_Integer index2 = 0; + for (Py::List::iterator it1 = list.begin(); it1 != list.end(); ++it1) { + index1++; + index2=0; + Py::List row(*it1); + for (Py::List::iterator it2 = row.begin(); it2 != row.end(); ++it2) { + index2++; + if(len == 1){ + Py::Vector v(*it2); + Base::Vector3d pnt = v.toVector(); + gp_Pnt newPoint(pnt.x,pnt.y,pnt.z); + interpolationPoints.SetValue(index1, index2, newPoint); + } + else { + Standard_Real val = PyFloat_AsDouble((*it2).ptr()); + zPoints.SetValue(index1, index2, val); + } } } @@ -1279,7 +1378,12 @@ PyObject* BSplineSurfacePy::interpolate(PyObject *args) } GeomAPI_PointsToBSplineSurface surInterpolation; - surInterpolation.Interpolate (interpolationPoints); + if(len == 1){ + surInterpolation.Interpolate (interpolationPoints); + } + else { + surInterpolation.Interpolate(zPoints, X0, dX, Y0, dY); + } Handle_Geom_BSplineSurface sur(surInterpolation.Surface()); this->getGeomBSplineSurfacePtr()->setHandle(sur); Py_Return; diff --git a/src/Mod/Part/Gui/DlgPrimitives.cpp b/src/Mod/Part/Gui/DlgPrimitives.cpp index 3e4628646..7ef822edf 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.cpp +++ b/src/Mod/Part/Gui/DlgPrimitives.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -270,6 +271,9 @@ DlgPrimitives::~DlgPrimitives() void DlgPrimitives::pickCallback(void * ud, SoEventCallback * n) { const SoMouseButtonEvent * mbe = static_cast(n->getEvent()); + Picker* pick = reinterpret_cast(ud); + if (pick->exitCode >= 0) + pick->loop.exit(pick->exitCode); // Mark all incoming mouse button events as handled, especially, to deactivate the selection node n->setHandled(); @@ -277,16 +281,14 @@ void DlgPrimitives::pickCallback(void * ud, SoEventCallback * n) if (mbe->getState() == SoButtonEvent::DOWN) { const SoPickedPoint * point = n->getPickedPoint(); if (point) { - Picker* pick = reinterpret_cast(ud); if (pick->pickedPoint(point)) { - pick->loop.exit(0); + pick->exitCode = 0; } } } } else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2) { if (mbe->getState() == SoButtonEvent::UP) { - Picker* pick = reinterpret_cast(ud); pick->loop.exit(1); } } @@ -305,9 +307,17 @@ void DlgPrimitives::executeCallback(Picker* p) if (!viewer->isEditing()) { viewer->setEditing(true); viewer->setRedirectToSceneGraph(true); + SoNode* root = viewer->getSceneGraph(); + int mode; + if (root && root->getTypeId().isDerivedFrom(Gui::SoFCUnifiedSelection::getClassTypeId())) { + mode = static_cast(root)->selectionMode.getValue(); + static_cast(root)->selectionMode.setValue(Gui::SoFCUnifiedSelection::OFF); + } viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), pickCallback, p); this->setDisabled(true); int ret = p->loop.exec(); + if (root && root->getTypeId().isDerivedFrom(Gui::SoFCUnifiedSelection::getClassTypeId())) + static_cast(root)->selectionMode.setValue(mode); this->setEnabled(true); viewer->setEditing(false); viewer->setRedirectToSceneGraph(false); @@ -587,6 +597,9 @@ Location::~Location() viewer->setEditing(false); viewer->setRedirectToSceneGraph(false); viewer->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), pickCallback,this); + SoNode* root = viewer->getSceneGraph(); + if (root && root->getTypeId().isDerivedFrom(Gui::SoFCUnifiedSelection::getClassTypeId())) + static_cast(root)->selectionMode.setValue(this->mode); } } @@ -605,6 +618,11 @@ void Location::on_viewPositionButton_clicked() viewer->setEditing(true); viewer->setRedirectToSceneGraph(true); viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), pickCallback, this); + SoNode* root = viewer->getSceneGraph(); + if (root && root->getTypeId().isDerivedFrom(Gui::SoFCUnifiedSelection::getClassTypeId())) { + this->mode = static_cast(root)->selectionMode.getValue(); + static_cast(root)->selectionMode.setValue(Gui::SoFCUnifiedSelection::OFF); + } } } } @@ -617,15 +635,7 @@ void Location::pickCallback(void * ud, SoEventCallback * n) // Mark all incoming mouse button events as handled, especially, to deactivate the selection node n->getAction()->setHandled(); if (mbe->getButton() == SoMouseButtonEvent::BUTTON1) { - if (mbe->getState() == SoButtonEvent::UP) { - n->setHandled(); - view->setEditing(false); - view->setRedirectToSceneGraph(false); - Location* dlg = reinterpret_cast(ud); - dlg->activeView = 0; - view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), pickCallback,ud); - } - else if (mbe->getState() == SoButtonEvent::DOWN) { + if (mbe->getState() == SoButtonEvent::DOWN) { const SoPickedPoint * point = n->getPickedPoint(); if (point) { SbVec3f pnt = point->getPoint(); @@ -637,6 +647,19 @@ void Location::pickCallback(void * ud, SoEventCallback * n) } } } + else if (mbe->getButton() == SoMouseButtonEvent::BUTTON2) { + if (mbe->getState() == SoButtonEvent::UP) { + n->setHandled(); + view->setEditing(false); + view->setRedirectToSceneGraph(false); + Location* dlg = reinterpret_cast(ud); + dlg->activeView = 0; + view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), pickCallback,ud); + SoNode* root = view->getSceneGraph(); + if (root && root->getTypeId().isDerivedFrom(Gui::SoFCUnifiedSelection::getClassTypeId())) + static_cast(root)->selectionMode.setValue(static_cast(ud)->mode); + } + } } QString Location::toPlacement() const diff --git a/src/Mod/Part/Gui/DlgPrimitives.h b/src/Mod/Part/Gui/DlgPrimitives.h index 178dff08a..4be65c31a 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.h +++ b/src/Mod/Part/Gui/DlgPrimitives.h @@ -39,7 +39,7 @@ namespace PartGui { class Picker { public: - Picker() + Picker() : exitCode(-1) { } virtual ~Picker() @@ -51,6 +51,7 @@ public: void createPrimitive(QWidget* widget, const QString&, Gui::Document*); QString toPlacement(const gp_Ax2&) const; + int exitCode; QEventLoop loop; }; @@ -88,6 +89,7 @@ private Q_SLOTS: private: static void pickCallback(void * ud, SoEventCallback * n); + int mode; QPointer activeView; Ui_Location ui; }; diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index d73afe296..74aadfad5 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -142,6 +142,26 @@ void ViewProviderBoolean::updateData(const App::Property* prop) this->DiffuseColor.setValues(colBool); } } + else if (prop->getTypeId() == App::PropertyLink::getClassTypeId()) { + App::DocumentObject *pBase = static_cast(prop)->getValue(); + if (pBase) + Gui::Application::Instance->hideViewProvider(pBase); + } +} + +bool ViewProviderBoolean::onDelete(const std::vector &) +{ + // get the input shapes + Part::Boolean* pBool = static_cast(getObject()); + App::DocumentObject *pBase = pBool->Base.getValue(); + App::DocumentObject *pTool = pBool->Tool.getValue(); + + if (pBase) + Gui::Application::Instance->showViewProvider(pBase); + if (pTool) + Gui::Application::Instance->showViewProvider(pTool); + + return true; } PROPERTY_SOURCE(PartGui::ViewProviderMultiFuse,PartGui::ViewProviderPart) @@ -207,6 +227,26 @@ void ViewProviderMultiFuse::updateData(const App::Property* prop) if (setColor) this->DiffuseColor.setValues(colBool); } + else if (prop->getTypeId() == App::PropertyLinkList::getClassTypeId()) { + std::vector pShapes = static_cast(prop)->getValues(); + for (std::vector::iterator it = pShapes.begin(); it != pShapes.end(); ++it) { + if (*it) + Gui::Application::Instance->hideViewProvider(*it); + } + } +} + +bool ViewProviderMultiFuse::onDelete(const std::vector &) +{ + // get the input shapes + Part::MultiFuse* pBool = static_cast(getObject()); + std::vector pShapes = pBool->Shapes.getValues(); + for (std::vector::iterator it = pShapes.begin(); it != pShapes.end(); ++it) { + if (*it) + Gui::Application::Instance->showViewProvider(*it); + } + + return true; } @@ -273,4 +313,24 @@ void ViewProviderMultiCommon::updateData(const App::Property* prop) if (setColor) this->DiffuseColor.setValues(colBool); } + else if (prop->getTypeId() == App::PropertyLinkList::getClassTypeId()) { + std::vector pShapes = static_cast(prop)->getValues(); + for (std::vector::iterator it = pShapes.begin(); it != pShapes.end(); ++it) { + if (*it) + Gui::Application::Instance->hideViewProvider(*it); + } + } +} + +bool ViewProviderMultiCommon::onDelete(const std::vector &) +{ + // get the input shapes + Part::MultiCommon* pBool = static_cast(getObject()); + std::vector pShapes = pBool->Shapes.getValues(); + for (std::vector::iterator it = pShapes.begin(); it != pShapes.end(); ++it) { + if (*it) + Gui::Application::Instance->showViewProvider(*it); + } + + return true; } diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.h b/src/Mod/Part/Gui/ViewProviderBoolean.h index 29c531109..04a409f81 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.h +++ b/src/Mod/Part/Gui/ViewProviderBoolean.h @@ -43,6 +43,7 @@ public: std::vector claimChildren(void) const; QIcon getIcon(void) const; void updateData(const App::Property*); + bool onDelete(const std::vector &); }; /// ViewProvider for the MultiFuse feature @@ -60,6 +61,7 @@ public: std::vector claimChildren(void) const; QIcon getIcon(void) const; void updateData(const App::Property*); + bool onDelete(const std::vector &); }; /// ViewProvider for the MultiFuse feature @@ -77,6 +79,7 @@ public: std::vector claimChildren(void) const; QIcon getIcon(void) const; void updateData(const App::Property*); + bool onDelete(const std::vector &); }; diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index fa32a921d..841fb402b 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -243,6 +243,17 @@ std::vector ViewProviderFillet::claimChildren() const return temp; } +bool ViewProviderFillet::onDelete(const std::vector &) +{ + // get the input shape + Part::Fillet* pFillet = static_cast(getObject()); + App::DocumentObject *pBase = pFillet->Base.getValue(); + if (pBase) + Gui::Application::Instance->showViewProvider(pBase); + + return true; +} + // --------------------------------------- PROPERTY_SOURCE(PartGui::ViewProviderChamfer, PartGui::ViewProviderPart) @@ -296,6 +307,17 @@ std::vector ViewProviderChamfer::claimChildren() const return temp; } +bool ViewProviderChamfer::onDelete(const std::vector &) +{ + // get the input shape + Part::Chamfer* pChamfer = static_cast(getObject()); + App::DocumentObject *pBase = pChamfer->Base.getValue(); + if (pBase) + Gui::Application::Instance->showViewProvider(pBase); + + return true; +} + // --------------------------------------- PROPERTY_SOURCE(PartGui::ViewProviderRevolution, PartGui::ViewProviderPart) @@ -315,3 +337,14 @@ std::vector ViewProviderRevolution::claimChildren() const temp.push_back(static_cast(getObject())->Source.getValue()); return temp; } + +bool ViewProviderRevolution::onDelete(const std::vector &) +{ + // get the input shape + Part::Revolution* pRevolve = static_cast(getObject()); + App::DocumentObject *pBase = pRevolve->Source.getValue(); + if (pBase) + Gui::Application::Instance->showViewProvider(pBase); + + return true; +} diff --git a/src/Mod/Part/Gui/ViewProviderMirror.h b/src/Mod/Part/Gui/ViewProviderMirror.h index 603bedeac..7e282b01a 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.h +++ b/src/Mod/Part/Gui/ViewProviderMirror.h @@ -64,6 +64,7 @@ public: //@{ void setupContextMenu(QMenu*, QObject*, const char*); std::vector claimChildren() const; + bool onDelete(const std::vector &); protected: bool setEdit(int ModNum); @@ -84,6 +85,7 @@ public: //@{ void setupContextMenu(QMenu*, QObject*, const char*); std::vector claimChildren() const; + bool onDelete(const std::vector &); protected: bool setEdit(int ModNum); @@ -103,6 +105,7 @@ public: /// grouping handling std::vector claimChildren(void)const; + bool onDelete(const std::vector &); }; } // namespace PartGui diff --git a/src/Mod/Raytracing/Gui/Command.cpp b/src/Mod/Raytracing/Gui/Command.cpp index ef2affe17..aeebf3d24 100644 --- a/src/Mod/Raytracing/Gui/Command.cpp +++ b/src/Mod/Raytracing/Gui/Command.cpp @@ -102,8 +102,6 @@ void CmdRaytracingWriteCamera::activated(int iMsg) SoInput in; in.setBuffer((void*)ppReturn,std::strlen(ppReturn)); - //if (!in.openFile(filename)) { exit(1); } - SoNode* rootNode; SoDB::read(&in,rootNode); diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 32d718d21..c5b1bc982 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -224,7 +224,11 @@ Base::Vector3d SketchObject::getPoint(int GeoId, PointPos PosId) const assert(GeoId == H_Axis || GeoId == V_Axis || (GeoId <= getHighestCurveIndex() && GeoId >= -getExternalGeometryCount()) ); const Part::Geometry *geo = getGeometry(GeoId); - if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + if (geo->getTypeId() == Part::GeomPoint::getClassTypeId()) { + const Part::GeomPoint *p = dynamic_cast(geo); + if (PosId == start || PosId == mid || PosId == end) + return p->getPoint(); + } else if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { const Part::GeomLineSegment *lineSeg = dynamic_cast(geo); if (PosId == start) return lineSeg->getStartPoint(); diff --git a/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg b/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg index 648d602fb..b69c7cc25 100644 --- a/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg +++ b/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg @@ -14,8 +14,8 @@ height="64px" id="svg2726" sodipodi:version="0.32" - inkscape:version="0.48.1 r9760" - sodipodi:docname="Sketcher_Point.svg" + inkscape:version="0.48.3.1 r9886" + sodipodi:docname="Sketcher_CreatePoint.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" version="1.1" inkscape:export-filename="/home/yorik/Sources/FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg.png" @@ -87,6 +87,17 @@ id="radialGradient3888" xlink:href="#linearGradient3144-3" inkscape:collect="always" /> + + inkscape:window-width="657" + inkscape:window-height="716" + inkscape:window-x="710" + inkscape:window-y="24" + inkscape:window-maximized="0" /> @@ -115,7 +126,7 @@ image/svg+xml - + @@ -133,35 +144,29 @@ sodipodi:cy="655.2193" sodipodi:cx="197.14285" id="path3162-2" - style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.80000019000000044;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:0.67000001" + style="opacity:0.67000002;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:5.09976673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" sodipodi:type="arc" - transform="matrix(-2.047093,-0.04650869,0.04650869,-2.047093,549.55577,1983.2711)" /> - - - - + transform="matrix(-1.135537,-0.02584694,0.0257987,-1.13766,378.69194,1376.1392)" /> + + diff --git a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h index 506bf98d2..79e2f77e1 100644 --- a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h +++ b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.h @@ -65,8 +65,6 @@ public: protected: ViewProviderSketch *sketchView; - std::string documentName; - TaskSketcherConstrains *Constraints; TaskSketcherGeneral *General; TaskSketcherMessages *Messages; diff --git a/src/Tools/makedist.py b/src/Tools/makedist.py index e42c69a33..66c4a3ea9 100644 --- a/src/Tools/makedist.py +++ b/src/Tools/makedist.py @@ -34,6 +34,7 @@ def main(): gitattr.write("zipios++ export-ignore\n") gitattr.write("Pivy-0.5 export-ignore\n") gitattr.write("Pivy export-ignore\n") + gitattr.write("3Dconnexion export-ignore\n") gitattr.write("Kuka export-ignore\n") gitattr.close()