diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c83b9d2d..91d447c10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,30 +209,64 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X) #first, look for OpenCASCADE Community Edition (OCE) #if OCE is installed in a nonstandard location, add -DOCE_DIR=/path/to/dir/containing/OCEConfig.cmake # when configuring with cmake, i.e. cmake .. -DOCE_DIR=/usr/share/cmake - if( NOT DEFINED OCE_DIR ) - if( UNIX ) - set( OCE_DIR "/usr/local/share/cmake/" ) - else() - set( OCE_DIR "c:/OCE-0.4.0/share/cmake" ) - endif() - endif() - find_package ( OCE ) - if( ${OCE_FOUND} ) - message("-- OpenCASCADE Community Edition has been found.") - add_definitions ( -DHAVE_CONFIG_H ) - 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_OCAF_LIBRARIES "TKCAF;TKXCAF;TKLCAF;TKXDESTEP;TKXDEIGES;TKMeshVS;TKAdvTools" ) #lib list copied from FreeCAD's FindOpenCasCade.cmake - set( OCC_INCLUDE_DIR ${OCE_INCLUDE_DIRS} ) - set( OCC_FOUND ${OCE_FOUND} ) - else() #look for OpenCASCADE - find_package(OpenCasCade) - IF(NOT OCC_FOUND) - MESSAGE("Neither OpenCASCADE Community Edition nor OpenCasCade were found: will not build CAD modules!") - ELSE() - MESSAGE("-- OpenCASCADE include directory: ${OCC_INCLUDE_PATH}") - MESSAGE("-- OpenCASCADE shared libraries directory: ${OCC_LIB_PATH}") - ENDIF() - endif() + if(NOT DEFINED OCE_DIR) + if(UNIX) + set(OCE_DIR "/usr/local/share/cmake/") + elseif(WIN32) + set(OCE_DIR "c:/OCE-0.4.0/share/cmake") + endif() + endif() + find_package (OCE QUIET) + if(${OCE_FOUND}) + message("-- OpenCASCADE Community Edition has been found.") + add_definitions (-DHAVE_CONFIG_H) + #lib list copied from FreeCAD's FindOpenCasCade.cmake + 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 + TKFeat + ) + #lib list copied from FreeCAD's FindOpenCasCade.cmake + set(OCC_OCAF_LIBRARIES + TKCAF + TKXCAF + TKLCAF + TKXDESTEP + TKXDEIGES + TKMeshVS + TKAdvTools + ) + set(OCC_INCLUDE_DIR ${OCE_INCLUDE_DIRS}) + set(OCC_FOUND ${OCE_FOUND}) + else() #look for OpenCASCADE + find_package(OpenCasCade) + if(NOT OCC_FOUND) + message("Neither OpenCASCADE Community Edition nor OpenCasCade were found: will not build CAD modules!") + else() + message("-- OpenCASCADE include directory: ${OCC_INCLUDE_PATH}") + message("-- OpenCASCADE shared libraries directory: ${OCC_LIB_PATH}") + endif() + endif() # -------------------------------- Salome SMESH -------------------------- @@ -362,6 +396,17 @@ MARK_AS_ADVANCED(FORCE FREECAD_LIBPACK_CHECKFILE6X FREECAD_LIBPACK_CHECKFILE7X) find_package(Spnav) endif(WIN32) +# -------------------------------- Shiboken/PySide ------------------------ + + find_package(Shiboken QUIET) + IF(SHIBOKEN_INCLUDE_DIR) + message("-- Shiboken has been found.") + ENDIF(SHIBOKEN_INCLUDE_DIR) + find_package(PySide QUIET) + IF(PYSIDE_INCLUDE_DIR) + message("-- PySide has been found.") + ENDIF(PYSIDE_INCLUDE_DIR) + # ------------------------------ Matplotlib ------------------------------ find_package(Matplotlib) @@ -441,8 +486,14 @@ IF(APPLE) SET(CMAKE_SHARED_LIBRARY_SUFFIX ".so") ENDIF(APPLE) +# force build directory to be different to source directory +#if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) +#MESSAGE(SEND_ERROR "The build directory (${CMAKE_BINARY_DIR}) must be different to the source directory " +# "(${CMAKE_SOURCE_DIR}). Please choose another build directory!") +#elseif() add_subdirectory(src) add_subdirectory(data) +#endif() # ================================================================================ # == Packaging =================================================================== diff --git a/README.Win32 b/README.Win32 index 6d379daf9..a91ad5885 100644 --- a/README.Win32 +++ b/README.Win32 @@ -83,3 +83,7 @@ because it contains some Fortran code. project. 9. Build the file vcf2c.lib with "nmake -f makefile.vc" and add it to the MEFISTO2 project as additional library. The linker errors should now go away. + +* FreeType + +http://stackoverflow.com/questions/6207176/compiling-freetype-to-dll-as-opposed-to-static-library diff --git a/cMake/FindCoin3D.cmake b/cMake/FindCoin3D.cmake index 710768740..5f09c3dbe 100644 --- a/cMake/FindCoin3D.cmake +++ b/cMake/FindCoin3D.cmake @@ -13,6 +13,7 @@ IF (WIN32) FIND_PATH(COIN3D_INCLUDE_DIR Inventor/So.h /usr/include /usr/local/include + /usr/include/coin ) FIND_LIBRARY(COIN3D_LIBRARY Coin diff --git a/cMake/UseLibPack6x.cmake b/cMake/UseLibPack6x.cmake index ef6bc2011..847ede524 100644 --- a/cMake/UseLibPack6x.cmake +++ b/cMake/UseLibPack6x.cmake @@ -196,6 +196,8 @@ include(AddFileDependencies) macro(fc_wrap_cpp outfiles ) QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) + # fixes bug 0000585: bug with boost 1.48 + SET(moc_options ${moc_options} -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) SET(ARGN) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) diff --git a/cMake/UseLibPack7x.cmake b/cMake/UseLibPack7x.cmake index 6063ef3b3..2e6ba13f6 100644 --- a/cMake/UseLibPack7x.cmake +++ b/cMake/UseLibPack7x.cmake @@ -207,6 +207,8 @@ include(AddFileDependencies) macro(fc_wrap_cpp outfiles ) QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) + # fixes bug 0000585: bug with boost 1.48 + SET(moc_options ${moc_options} -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) SET(ARGN) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) diff --git a/cMake/UseLibPack8x.cmake b/cMake/UseLibPack8x.cmake index 4def5d1f7..0d6961d6d 100644 --- a/cMake/UseLibPack8x.cmake +++ b/cMake/UseLibPack8x.cmake @@ -219,6 +219,8 @@ include(AddFileDependencies) macro(fc_wrap_cpp outfiles ) QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) + # fixes bug 0000585: bug with boost 1.48 + SET(moc_options ${moc_options} -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) SET(ARGN) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) @@ -392,5 +394,19 @@ SET(EIGEN3_INCLUDE_DIR ${FREECAD_LIBPACK_DIR}/include/eigen3) set(EIGEN3_FOUND TRUE) - - +# FreeType +if(FREECAD_USE_FREETYPE) + set(FREETYPE_LIBRARIES + optimized ${FREECAD_LIBPACK_DIR}/lib/freetype.lib + debug ${FREECAD_LIBPACK_DIR}/lib/freetyped.lib + ) + set(FREETYPE_INCLUDE_DIRS + ${FREECAD_LIBPACK_DIR}/include/FreeType-2.4.12 + ) + set(FREETYPE_VERSION_STRING + "2.4.12" + ) + set(FREETYPE_FOUND + TRUE + ) +endif(FREECAD_USE_FREETYPE) diff --git a/cMake/UseLibPackCustom.cmake b/cMake/UseLibPackCustom.cmake index 740291a02..986ca9fd4 100644 --- a/cMake/UseLibPackCustom.cmake +++ b/cMake/UseLibPackCustom.cmake @@ -230,6 +230,8 @@ include(AddFileDependencies) macro(fc_wrap_cpp outfiles ) QT4_EXTRACT_OPTIONS(moc_files moc_options ${ARGN}) + # fixes bug 0000585: bug with boost 1.48 + SET(moc_options ${moc_options} -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED) SET(ARGN) foreach(it ${moc_files}) get_filename_component(it ${it} ABSOLUTE) @@ -321,72 +323,72 @@ set(NGLIB_DEBUG_LIBRARIES #set(OCC_INCLUDE_DIR C:/Projects/LibPack/oce-0.10.0/include/oce) #set(OCC_LIBRARY_DIR C:/Projects/LibPack/oce-0.10.0/Win64/lib) #set(OCC_LIBRARIES -# ${OCC_LIBRARY_DIR}/TKFillet.lib -# ${OCC_LIBRARY_DIR}/TKMesh.lib -# ${OCC_LIBRARY_DIR}/TKernel.lib -# ${OCC_LIBRARY_DIR}/TKG2d.lib -# ${OCC_LIBRARY_DIR}/TKG3d.lib -# ${OCC_LIBRARY_DIR}/TKMath.lib -# ${OCC_LIBRARY_DIR}/TKIGES.lib -# ${OCC_LIBRARY_DIR}/TKSTL.lib -# ${OCC_LIBRARY_DIR}/TKShHealing.lib -# ${OCC_LIBRARY_DIR}/TKXSBase.lib -# ${OCC_LIBRARY_DIR}/TKBool.lib -# ${OCC_LIBRARY_DIR}/TKBO.lib -# ${OCC_LIBRARY_DIR}/TKBRep.lib -# ${OCC_LIBRARY_DIR}/TKTopAlgo.lib -# ${OCC_LIBRARY_DIR}/TKGeomAlgo.lib -# ${OCC_LIBRARY_DIR}/TKGeomBase.lib -# ${OCC_LIBRARY_DIR}/TKOffset.lib -# ${OCC_LIBRARY_DIR}/TKPrim.lib -# ${OCC_LIBRARY_DIR}/TKSTEP.lib -# ${OCC_LIBRARY_DIR}/TKSTEPBase.lib -# ${OCC_LIBRARY_DIR}/TKSTEPAttr.lib -# ${OCC_LIBRARY_DIR}/TKHLR.lib -# ${OCC_LIBRARY_DIR}/TKFeat.lib +# ${OCC_LIBRARY_DIR}/TKFillet.lib +# ${OCC_LIBRARY_DIR}/TKMesh.lib +# ${OCC_LIBRARY_DIR}/TKernel.lib +# ${OCC_LIBRARY_DIR}/TKG2d.lib +# ${OCC_LIBRARY_DIR}/TKG3d.lib +# ${OCC_LIBRARY_DIR}/TKMath.lib +# ${OCC_LIBRARY_DIR}/TKIGES.lib +# ${OCC_LIBRARY_DIR}/TKSTL.lib +# ${OCC_LIBRARY_DIR}/TKShHealing.lib +# ${OCC_LIBRARY_DIR}/TKXSBase.lib +# ${OCC_LIBRARY_DIR}/TKBool.lib +# ${OCC_LIBRARY_DIR}/TKBO.lib +# ${OCC_LIBRARY_DIR}/TKBRep.lib +# ${OCC_LIBRARY_DIR}/TKTopAlgo.lib +# ${OCC_LIBRARY_DIR}/TKGeomAlgo.lib +# ${OCC_LIBRARY_DIR}/TKGeomBase.lib +# ${OCC_LIBRARY_DIR}/TKOffset.lib +# ${OCC_LIBRARY_DIR}/TKPrim.lib +# ${OCC_LIBRARY_DIR}/TKSTEP.lib +# ${OCC_LIBRARY_DIR}/TKSTEPBase.lib +# ${OCC_LIBRARY_DIR}/TKSTEPAttr.lib +# ${OCC_LIBRARY_DIR}/TKHLR.lib +# ${OCC_LIBRARY_DIR}/TKFeat.lib #) #set(OCC_OCAF_LIBRARIES -# ${OCC_LIBRARY_DIR}/TKCAF.lib -# ${OCC_LIBRARY_DIR}/TKXCAF.lib -# ${OCC_LIBRARY_DIR}/TKLCAF.lib -# ${OCC_LIBRARY_DIR}/TKXDESTEP.lib -# ${OCC_LIBRARY_DIR}/TKXDEIGES.lib +# ${OCC_LIBRARY_DIR}/TKCAF.lib +# ${OCC_LIBRARY_DIR}/TKXCAF.lib +# ${OCC_LIBRARY_DIR}/TKLCAF.lib +# ${OCC_LIBRARY_DIR}/TKXDESTEP.lib +# ${OCC_LIBRARY_DIR}/TKXDEIGES.lib #) set(OCC_INCLUDE_DIR ${FREECAD_LIBPACK_DIR}/include/OpenCascade-6.3.0) set(OCC_LIBRARY_DIR ${FREECAD_LIBPACK_DIR}/lib) 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 - TKFeat + TKFillet + TKMesh + TKernel + TKG2d + TKG3d + TKMath + TKIGES + TKSTL + TKShHealing + TKXSBase + TKBool + TKBO + TKBRep + TKTopAlgo + TKGeomAlgo + TKGeomBase + TKOffset + TKPrim + TKSTEP + TKSTEPBase + TKSTEPAttr + TKHLR + TKFeat ) set(OCC_OCAF_LIBRARIES - TKCAF - TKXCAF - TKLCAF - TKXDESTEP - TKXDEIGES - TKMeshVS - TKAdvTools + TKCAF + TKXCAF + TKLCAF + TKXDESTEP + TKXDEIGES + TKMeshVS + TKAdvTools ) set(OCC_FOUND TRUE) @@ -400,5 +402,19 @@ set(ODE_INCLUDE_DIRS ${FREECAD_LIBPACK_DIR}/include/ode-0.11.1) set(ODE_LIBRARIES ${FREECAD_LIBPACK_DIR}/lib/ode_double.lib) set(ODE_FOUND TRUE) - - +# FreeType +if(FREECAD_USE_FREETYPE) + set(FREETYPE_LIBRARIES + optimized ${FREECAD_LIBPACK_DIR}/lib/freetype.lib + debug ${FREECAD_LIBPACK_DIR}/lib/freetyped.lib + ) + set(FREETYPE_INCLUDE_DIRS + ${FREECAD_LIBPACK_DIR}/include/FreeType-2.4.12 + ) + set(FREETYPE_VERSION_STRING + "2.4.12" + ) + set(FREETYPE_FOUND + TRUE + ) +endif(FREECAD_USE_FREETYPE) diff --git a/data/examples/CMakeLists.txt b/data/examples/CMakeLists.txt index eb0e2f991..4df175800 100644 --- a/data/examples/CMakeLists.txt +++ b/data/examples/CMakeLists.txt @@ -12,7 +12,10 @@ ADD_CUSTOM_TARGET(Example_data ALL SOURCES ${Examples_Files} ) -fc_copy_sources(Examples "${CMAKE_BINARY_DIR}/data/examples" ${Examples_Files}) +# 0001097: CMake stops with error "Circular ... <- ... dependency dropped." +if(NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") + fc_copy_sources(Example_data "${CMAKE_BINARY_DIR}/data/examples" ${Examples_Files}) +endif() INSTALL( FILES diff --git a/package/debian/changelog b/package/debian/changelog index 88e234be9..6ff7e60de 100644 --- a/package/debian/changelog +++ b/package/debian/changelog @@ -1,3 +1,18 @@ +freecad (0.13-1precise2) precise; urgency=low + + * Fix hard dependency on libgl1-mesa-glx preventing installation on + Ubuntu 12.04.2 with LTSEnablementStack. Removal of shlibs.local file + and libgl1-mesa-dev deleted from BuildDeps. + * Removed version number from libsoqt4-dev BD. + + -- Normand Chamberland Sun, 05 May 2013 18:31:47 -0400 + +freecad (0.13-1precise1) precise; urgency=low + + * New release for Ubuntu 12.04 (precise) + + -- Normand Chamberland Sun, 06 May 2012 14:38:12 -0400 + freecad (0.11.3729.dfsg-2) unstable; urgency=low * Add gfortran and libopencascade-visualization-dev to BD @@ -29,6 +44,25 @@ freecad (0.11.3729.dfsg-1) unstable; urgency=low -- "Adam C. Powell, IV" Tue, 12 Apr 2011 23:40:30 -0400 +freecad (0.10.3247.dfsg-2ubuntu3) natty; urgency=low + + * Fix build failure with ld --as-needed. + + -- Matthias Klose Wed, 15 Dec 2010 01:12:39 +0100 + +freecad (0.10.3247.dfsg-2ubuntu2) natty; urgency=low + + * Rebuild with python 2.7 as the python default. + + -- Matthias Klose Thu, 09 Dec 2010 16:46:45 +0000 + +freecad (0.10.3247.dfsg-2ubuntu1) natty; urgency=low + + * Merge from debian unstable. Remaining changes: + - build on libqtwebkit-dev for qtwebkit transition + + -- Bhavani Shankar Wed, 20 Oct 2010 08:40:53 +0530 + freecad (0.10.3247.dfsg-2) unstable; urgency=low * control: @@ -43,6 +77,20 @@ freecad (0.10.3247.dfsg-2) unstable; urgency=low -- Teemu Ikonen Wed, 18 Aug 2010 19:34:36 +0200 +freecad (0.10.3247.dfsg-1ubuntu2) maverick; urgency=low + + * Rebuild on libqtwebkit-dev for qtwebkit transition + + -- Jonathan Riddell Wed, 21 Jul 2010 10:06:31 +0100 + +freecad (0.10.3247.dfsg-1ubuntu1) maverick; urgency=low + + * Merge from Debian unstable, remaining changes: + - debian/control: Build-Depends on libqt4-webkit-dev due to + QtWebKit is no longer part of libqt4-dev (LP: #604078) + + -- Artur Rona Sat, 10 Jul 2010 21:06:47 +0200 + freecad (0.10.3247.dfsg-1) unstable; urgency=low * New upstream version (closes: #582627) @@ -57,6 +105,12 @@ freecad (0.10.3247.dfsg-1) unstable; urgency=low -- Teemu Ikonen Mon, 05 Jul 2010 15:07:49 +0200 +freecad (0.9.2646.5.dfsg-1ubuntu1) maverick; urgency=low + + * Add build-dep on libqt4-webkit-dev to fix FTBFS with Qt 4.7 + + -- Scott Kitterman Sat, 19 Jun 2010 00:37:12 -0400 + freecad (0.9.2646.5.dfsg-1) unstable; urgency=low * Add 'dfsg' extension to upstream version, upstream sources are unchanged. diff --git a/package/debian/control b/package/debian/control index 6e539444f..b2909693e 100644 --- a/package/debian/control +++ b/package/debian/control @@ -5,25 +5,26 @@ Maintainer: Debian Science Maintainers , "Adam C. Powell, IV" , Anton Gladky Vcs-Browser: http://git.debian.org/?p=debian-science/packages/freecad.git Vcs-Git: git://git.debian.org/git/debian-science/packages/freecad.git -Homepage: http://juergen-riegel.net/FreeCAD/Docu/index.php?title=Main_Page -Build-Depends: debhelper (>= 7.0.50~), autotools-dev, libtool, automake, - autoconf, libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, +Homepage: https://sourceforge.net/apps/mediawiki/free-cad/index.php?title=Main_Page +Build-Depends: debhelper (>= 7.0.50~), cmake, + libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, libboost-graph-dev, libboost-iostreams-dev, libboost-program-options-dev, libboost-regex-dev, libboost-serialization-dev, libboost-signals-dev, - libboost-python-dev, python-dev, python-support, + libboost-thread-dev, libboost-python-dev, python-dev, python-support, libqt4-dev, libxt-dev, libxext-dev, libxmu-dev, libxi-dev, libx11-dev, - libcoin60-dev, libsoqt4-dev (>= 1.4.2~svn20090224), libeigen3-dev, libgl1-mesa-dev, - zlib1g-dev, libxerces-c2-dev, libopencascade-foundation-dev, libopencascade-modeling-dev, + libcoin60-dev, libsoqt4-dev, libeigen3-dev, + zlib1g-dev, libxerces-c2-dev, libopencascade-foundation-dev, + libopencascade-modeling-dev, libopencascade-ocaf-dev, libopencascade-visualization-dev, python-cxx-dev, libswscale-dev, - libzipios++-dev, swig, gfortran, libqtwebkit-dev -Standards-Version: 3.9.2 + libzipios++-dev, swig, gfortran, f2c, libqtwebkit-dev, libspnav-dev, libfreetype6-dev +Standards-Version: 3.9.1 Package: freecad Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} -Recommends: python-pivy python-matplotlib +Recommends: python-pivy, python Suggests: freecad-doc -Description: Extensible Open Source CAx program (alpha) +Description: Extensible Open Source CAx program (beta) FreeCAD is an Open Source CAx RAD based on OpenCasCade, Qt and Python. It features some key concepts like macro recording, workbenches, ability to run as a server and dynamically loadable application extensions and diff --git a/package/debian/diff/freecad_precise.diff b/package/debian/diff/freecad_precise.diff new file mode 100644 index 000000000..a8fda14b0 --- /dev/null +++ b/package/debian/diff/freecad_precise.diff @@ -0,0 +1,1018 @@ +--- freecad-0.13.orig/debian/changelog ++++ freecad-0.13/debian/changelog +@@ -0,0 +1,281 @@ ++freecad (0.13-1precise2) precise; urgency=low ++ ++ * Fix hard dependency on libgl1-mesa-glx preventing installation on ++ Ubuntu 12.04.2 with LTSEnablementStack. Removal of shlibs.local file ++ and libgl1-mesa-dev deleted from BuildDeps. ++ * Removed version number from libsoqt4-dev BD. ++ ++ -- Normand Chamberland Sun, 05 May 2013 18:31:47 -0400 ++ ++freecad (0.13-1precise1) precise; urgency=low ++ ++ * New release for Ubuntu 12.04 (precise) ++ ++ -- Normand Chamberland Sun, 06 May 2012 14:38:12 -0400 ++ ++freecad (0.11.3729.dfsg-2) unstable; urgency=low ++ ++ * Add gfortran and libopencascade-visualization-dev to BD ++ to fix FTBFS (closes: #622694) ++ * Add libqtwebkit-dev to BD (closes: #618241) ++ * Delete quilt from BD and corresponding changes in rules. ++ * Add description to freecad-occ650.patch ++ * Delete encoding string from .desktop ++ * Fix some spelling errors, pointed out by lintian. ++ ++ -- Anton Gladky Thu, 14 Apr 2011 10:23:25 +0100 ++ ++freecad (0.11.3729.dfsg-1) unstable; urgency=low ++ ++ [ Denis Barbier ] ++ * Merge OpenCASCADE 6.5.0 compatibility patch (closes: #617545). ++ ++ [ Adam C. Powell, IV ] ++ * New upstream (closes: #622213, #618241). ++ * Changed to source format 3.0 (quilt). ++ * Added patch target which forces autotools to run, and automake and autoconf ++ are now in Build-Depends (closes: #607181). ++ * Set aside src/Build/Version.h to prevent build problems. ++ * Does not install .la files (closes: #621298). ++ * Boost 1.46 compatibility patch (closes: #621877). ++ * Set aside files which autotools modifies so clean works. ++ * Added libtool to Build-Depends (thanks: PICCA Frédéric-Emmanuel). ++ * Bumped Standards-Version, no changes needed. ++ ++ -- "Adam C. Powell, IV" Tue, 12 Apr 2011 23:40:30 -0400 ++ ++freecad (0.10.3247.dfsg-2ubuntu3) natty; urgency=low ++ ++ * Fix build failure with ld --as-needed. ++ ++ -- Matthias Klose Wed, 15 Dec 2010 01:12:39 +0100 ++ ++freecad (0.10.3247.dfsg-2ubuntu2) natty; urgency=low ++ ++ * Rebuild with python 2.7 as the python default. ++ ++ -- Matthias Klose Thu, 09 Dec 2010 16:46:45 +0000 ++ ++freecad (0.10.3247.dfsg-2ubuntu1) natty; urgency=low ++ ++ * Merge from debian unstable. Remaining changes: ++ - build on libqtwebkit-dev for qtwebkit transition ++ ++ -- Bhavani Shankar Wed, 20 Oct 2010 08:40:53 +0530 ++ ++freecad (0.10.3247.dfsg-2) unstable; urgency=low ++ ++ * control: ++ - Update to standars-version 3.9.0. ++ - Remove libblas-dev, libatlas-dev from build-deps. ++ * Add debian/shlibs.local file containing the the correct binary dep ++ to libsoqt4-20 (closes: #575239). ++ * copyright: Add a verbatim copy of Tiddlywiki BSD license. Fixes ++ the lintian warning copyright-refers-to-deprecated-bsd-license-file. ++ * Add kFreeBSD portability fixes. Thanks to Petr Salinger ++ for the patch (closes: #592461). ++ ++ -- Teemu Ikonen Wed, 18 Aug 2010 19:34:36 +0200 ++ ++freecad (0.10.3247.dfsg-1ubuntu2) maverick; urgency=low ++ ++ * Rebuild on libqtwebkit-dev for qtwebkit transition ++ ++ -- Jonathan Riddell Wed, 21 Jul 2010 10:06:31 +0100 ++ ++freecad (0.10.3247.dfsg-1ubuntu1) maverick; urgency=low ++ ++ * Merge from Debian unstable, remaining changes: ++ - debian/control: Build-Depends on libqt4-webkit-dev due to ++ QtWebKit is no longer part of libqt4-dev (LP: #604078) ++ ++ -- Artur Rona Sat, 10 Jul 2010 21:06:47 +0200 ++ ++freecad (0.10.3247.dfsg-1) unstable; urgency=low ++ ++ * New upstream version (closes: #582627) ++ * Add debian/source/format file with contents "1.0". ++ * Use freecad.xpm as icon in menu and desktop files. ++ * copyright: Add licensing information for new files in this release. ++ * src/Base/Makefile.in, src/Gui/Makefile.in: Do not remove *.tab.c files ++ in make distclean target. ++ * control: ++ - Add build-dep to libeigen2-dev. ++ - Update to standards-version 3.8.4. ++ ++ -- Teemu Ikonen Mon, 05 Jul 2010 15:07:49 +0200 ++ ++freecad (0.9.2646.5.dfsg-1ubuntu1) maverick; urgency=low ++ ++ * Add build-dep on libqt4-webkit-dev to fix FTBFS with Qt 4.7 ++ ++ -- Scott Kitterman Sat, 19 Jun 2010 00:37:12 -0400 ++ ++freecad (0.9.2646.5.dfsg-1) unstable; urgency=low ++ ++ * Add 'dfsg' extension to upstream version, upstream sources are unchanged. ++ * Remove libgl1-mesa-dev build-dep, rely on libcoin to pull in GL libraries. ++ * Change build-dep libatlas-headers to libatlas-dev (closes: #577309). ++ ++ -- Teemu Ikonen Fri, 14 May 2010 17:20:35 +0200 ++ ++freecad (0.9.2646.5-1) unstable; urgency=low ++ ++ * New upstream version (closes: #561696). ++ * Added swig to Build-Depends (closes: #563523, #563772). ++ * Removed python-opencv from Build-Depends and Recommends (closes: #560768). ++ ++ -- Adam C. Powell, IV Mon, 11 Jan 2010 08:48:33 -0500 ++ ++freecad (0.9.2646.4-1) unstable; urgency=low ++ ++ * New upstream version (closes: #559849, #559846). ++ ++ -- Adam C. Powell, IV Fri, 11 Dec 2009 20:21:32 -0500 ++ ++freecad (0.9.2646.3-1) unstable; urgency=low ++ ++ * New upstream version. Removes TiddlySaver.jar, fixes help problems. ++ ++ -- Teemu Ikonen Thu, 03 Dec 2009 19:39:27 +0100 ++ ++freecad (0.9.2646-1) unstable; urgency=low ++ ++ [ Werner Mayer ] ++ * New upstream release ++ * In-source copy of PyCXX has been dropped (closes: #547936) ++ * In-source copy of zipios++ has been dropped (closes: #547941) ++ * Change build-dependency on python2.5-dev to python-dev ++ * Add freecad-doc binary package ++ * Remove Suggestion of a chm viewer, suggest freecad-doc instead ++ ++ [ Teemu Ikonen ] ++ * Add override to dh_compress ++ * Add versioned build-deb to debhelper (>= 7.0.50) ++ * Add build-deps to libzipios++-dev and python-cxx-dev. ++ ++ -- Teemu Ikonen Tue, 17 Nov 2009 14:22:00 +0100 ++ ++freecad (0.8.2237-2) unstable; urgency=low ++ ++ * Added libboost-python-dev to Build-Depends (closes: #549738). ++ * Added myself to uploaders list. ++ * Bumped Standards-Version. ++ ++ -- Adam C. Powell, IV Thu, 12 Nov 2009 12:02:42 -0500 ++ ++freecad (0.8.2237-1) unstable; urgency=low ++ ++ * New Upstream release ++ ++ -- Teemu Ikonen Thu, 16 Jul 2009 18:37:41 +0200 ++ ++freecad (0.7.1658-1) UNRELEASED; urgency=low ++ ++ * New upstream release ++ ++ -- Teemu Ikonen Mon, 20 Oct 2008 15:35:58 +0200 ++ ++freecad (0.7.1514-1) UNRELEASED; urgency=low ++ ++ * New upstream version ++ * Add more stuff to the copyright file ++ * control: add build-dep to python-central ++ ++ -- Teemu Ikonen Wed, 06 Aug 2008 18:25:02 +0200 ++ ++freecad (0.7.1350-1hardy1) UNRELEASED; urgency=low ++ ++ * Package for Ubuntu 8.04 (Hardy Heron) ++ ++ -- Werner Mayer Thu, 29 May 2008 11:11:20 +0200 ++ ++freecad (0.7.1350-1gutsy1) UNRELEASED; urgency=low ++ ++ * Backport to Ubuntu 7.10 (Gutsy Gibbon) ++ ++ -- Werner Mayer Sat, 24 May 2008 01:54:39 +0200 ++ ++freecad (0.7.1350-1feisty1) UNRELEASED; urgency=low ++ ++ * Backport to Ubuntu 7.04 (Feisty Fawn) ++ ++ -- Werner Mayer Sat, 24 May 2008 00:09:08 +0200 ++ ++freecad (0.7.1350-1) UNRELEASED; urgency=low ++ ++ * New upstream release from sf.net ++ * Import to debian-science repository at git.debian.org ++ * control: ++ - Update to standards-version 3.7.3 ++ - Add Vcs-* fields pointing to git.debian.org ++ - Improve descriptions ++ * Convert copyright to (pseudo) machine readable format, audit source ++ * Fix categories in .desktop file ++ * Change Section to Science/Engineering in .doc-base and menu files ++ * rules: do not ignore errors on clean target, add dh_desktop call ++ -- Teemu Ikonen Tue, 05 Aug 2008 18:58:07 +0200 ++ ++freecad (0.7.1344-1ubuntu2) UNRELEASED; urgency=low ++ ++ * New package with fixed self-dependency problem ++ ++ -- Werner Mayer Thu, 22 May 2008 15:34:34 +0200 ++ ++freecad (0.7.1344-1ubuntu1) UNRELEASED; urgency=low ++ ++ * New debian package for Feisty ++ ++ -- Werner Mayer Thu, 22 May 2008 11:04:06 +0200 ++ ++freecad (0.7.1344-1) UNRELEASED; urgency=low ++ ++ * Write patch file to make builds with OpenCASCADE libs inside but with no ++ dependency to libopencascade6.2 ++ ++ -- Werner Mayer Wed, 21 May 2008 10:06:07 +0200 ++ ++freecad (0.7.1343-1) UNRELEASED; urgency=low ++ ++ * Embed required OpenCASCADE libs into this package as long as no official ++ Debian package is available ++ ++ -- Werner Mayer Tue, 20 May 2008 00:40:39 +0200 ++ ++freecad (0.7.1342-1) UNRELEASED; urgency=low ++ ++ * Switch to new versioning scheme of OpenCASCADE packages ++ ++ -- Werner Mayer Mon, 19 May 2008 23:55:31 +0200 ++ ++freecad (0.7.1316-1) UNRELEASED; urgency=low ++ ++ * Support of pivy (Python binding for Coin/SoQt) ++ * Support of PyQt4 ++ * General support of SWIG modules ++ ++ -- Werner Mayer Sat, 26 Apr 2008 13:51:25 +0200 ++ ++freecad (0.7.1031-1) UNRELEASED; urgency=low ++ ++ * Qt4 port finished ++ * Support of Python binding for Qt4 ++ * Support of Python binding for Coin ++ * Support of entirely in Python written modules ++ * Added support of model driven architecture for Python binding ++ * Use boost's signal/slot mechanism to update data ++ ++ -- Werner Mayer Tue, 04 Jan 2008 13:50:37 +0200 ++ ++freecad (0.7.645-1) UNRELEASED; urgency=low ++ ++ * Qt4 port started ++ ++ -- Werner Mayer Tue, 24 Jul 2007 13:04:37 +0200 ++ ++freecad (0.6.472-1) UNRELEASED; urgency=low ++ ++ * Initial Release ++ ++ -- Werner Mayer Tue, 26 Sep 2006 16:55:15 +0200 ++ +--- freecad-0.13.orig/debian/freecad.1 ++++ freecad-0.13/debian/freecad.1 +@@ -0,0 +1,73 @@ ++.\" Hey, EMACS: -*- nroff -*- ++.\" First parameter, NAME, should be all caps ++.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection ++.\" other parameters are allowed: see man(7), man(1) ++.TH FREECAD 1 "July 25, 2007" freecad "Linux User's Manual" ++.\" Please adjust this date whenever revising the manpage. ++.\" ++.\" Some roff macros, for reference: ++.\" .nh disable hyphenation ++.\" .hy enable hyphenation ++.\" .ad l left justify ++.\" .ad b justify to both left and right margins ++.\" .nf disable filling ++.\" .fi enable filling ++.\" .br insert line break ++.\" .sp insert n+1 empty lines ++.\" for manpage-specific macros, see man(7) ++.SH NAME ++freecad \- An extensible Open Source CAx program for Unix/X11 ++.SH SYNOPSIS ++.B freecad ++.RI [ options ] " files" ++.br ++.B freecadcmd ++.RI [ options ] " files" ++.SH DESCRIPTION ++.B FreeCAD ++is an Open Source CAx RAD based on OpenCasCade, Qt and Python. It features ++some key concepts like macro recording, workbenches, ability to run as a ++server and dynamically loadable application extensions and it is designed ++to be platform independent. ++.\" TeX users may be more comfortable with the \fB\fP and ++.\" \fI\fP escape sequences to invode bold face and italics, ++.\" respectively. ++.SH USAGE ++\fBfreecad\fR starts with a GUI while \fBfreecadcmd\fR is only a pure command line version that starts a Python interpreter. ++.SH OPTIONS ++These programs follow the usual GNU command line syntax, with long ++options starting with two dashes (`-'). ++A summary of the options supported by \fBfreecad\fR is included below. ++.SS "Generic options" ++.TP ++\fB\-h, \-\-help\fR ++Show summary of options. ++.TP ++\fB\-v, \-\-version\fR ++Show version of program. ++.TP ++\fB\-c, \-\-console\fR ++Start in console mode. ++.TP ++\fB\-\-response\-file\fR \fIarg\fR ++Can be specified with '@name', too. ++ ++.SS "Configuration" ++.TP ++\fB\-l, \-\-write\-log\fR ++Write a log file. ++.TP ++\fB\-t, \-\-run\-test\fR \fIarg\fR ++Test level. ++.TP ++\fB\-M, \-\-module\-path\fR \fIarg\fR ++Additional module path. ++.TP ++\fB\-P, \-\-python\-path\fR \fIarg\fR ++Additional Python path. ++.SH SEE ALSO ++To get more information about \fBFreeCAD\fR, please visit \fIhttp://juergen\-riegel.net/FreeCAD/Docu/index.php/Main_Page\fR ++.SH BUGS ++To report a bug, please visit \fIhttp://free-cad.sf.net/\fR ++.SH AUTHOR ++This manual page was written by Werner Mayer. +--- freecad-0.13.orig/debian/rules ++++ freecad-0.13/debian/rules +@@ -0,0 +1,98 @@ ++#!/usr/bin/make -f ++# -*- makefile -*- ++ ++# Uncomment this to turn on verbose mode. ++#export DH_VERBOSE=1 ++ ++# For testing: fakeroot debian/rules binary ++ ++# These are used for cross-compiling and for saving the configure script ++# from having to guess our platform (since we know it already) ++DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) ++DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ++ ++CFLAGS = -Wall -g ++ ++ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) ++ CFLAGS += -O0 ++else ++ CFLAGS += -O2 ++endif ++ ++configure: ++ dh_testdir ++ cmake . \ ++-DFREECAD_BUILD_DEBIAN=ON \ ++-DCMAKE_INSTALL_PREFIX=/usr/lib/freecad \ ++-DCMAKE_INSTALL_MANDIR=/usr/share/man \ ++-DCMAKE_INSTALL_INFODIR=/usr/share/info \ ++-DCMAKE_INSTALL_DATADIR=/usr/share/freecad \ ++-DCMAKE_INSTALL_INCLUDEDIR=/usr/include/freecad \ ++-DCMAKE_INSTALL_DOCDIR=/usr/share/doc/freecad ++ ++build: build-stamp ++ ++build-stamp: configure ++ dh_testdir ++ $(MAKE) ++ touch $@ ++ ++clean: ++ dh clean ++ rm -f build-stamp ++ find -name '*.pyc' | xargs rm -f ++ find -name 'moc_*.cpp' | xargs rm -f ++ find -name 'ui_*.h' | xargs rm -f ++ find -name 'CMakeFiles' | xargs rm -rf ++ rm -f stamp-h1 ++ ++install: build install-stamp ++install-stamp: ++ dh_testdir ++ dh_testroot ++ dh_prep ++ dh_installdirs ++ $(MAKE) install/fast DESTDIR=$(CURDIR)/debian/tmp/freecad ++ # install the core system ++ dh_install -pfreecad debian/tmp/freecad/usr/share/freecad/* usr/share/freecad/ ++ # Desktop icons ++ dh_install -pfreecad debian/tmp/freecad/usr/share/freecad/freecad.xpm usr/share/pixmaps ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-16.png debian/freecad/usr/share/icons/hicolor/16x16/apps/freecad.png ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-32.png debian/freecad/usr/share/icons/hicolor/32x32/apps/freecad.png ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-32.png debian/freecad/usr/share/icons/hicolor/48x48/apps/freecad.png ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-64.png debian/freecad/usr/share/icons/hicolor/64x64/apps/freecad.png ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad.svg debian/freecad/usr/share/icons/hicolor/scalable/apps/freecad.svg ++ install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-doc.png debian/freecad/usr/share/icons/hicolor/64x64/mimetypes/application-x-extension-fcstd.png ++ ++ dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/bin usr/lib/freecad ++ dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib usr/lib/freecad ++ dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod usr/lib/freecad ++ dh_install debian/freecad.desktop usr/share/applications ++ dh_installman debian/freecad.1 debian/mime/freecad-thumbnailer.1 ++ dh_installchangelogs ChangeLog.txt ++ # install the headers ++ #dh_install -pfreecad-dev debian/tmp/freecad/usr/include/* usr/include ++ # install the help system ++ dh_install -pfreecad-doc debian/tmp/freecad/usr/share/doc/* usr/share/doc/ ++ # install MIME stuff ++ dh_install debian/mime/freecad-thumbnailer usr/bin ++ dh_install debian/mime/freecad.thumbnailer usr/share/thumbnailers ++ dh_install debian/mime/freecad.schemas etc/gconf/schemas ++ dh_gconf -pfreecad ++ dh_installmime ++ touch install-stamp ++ ++override_dh_compress: ++ dh_compress -X.qch -X.qhc ++ ++override_dh_makeshlibs: ++ ++ ++binary-indep: build install ++ dh binary-indep ++ ++binary-arch: build install ++ dh binary-arch ++ ++binary: binary-indep binary-arch ++.PHONY: build clean binary-indep binary-arch binary install +--- freecad-0.13.orig/debian/menu ++++ freecad-0.13/debian/menu +@@ -0,0 +1,6 @@ ++?package(freecad):needs="X11"\ ++ section="Applications/Science/Engineering"\ ++ title="FreeCAD"\ ++ command="/usr/bin/freecad"\ ++ icon="/usr/share/pixmaps/freecad.xpm" ++ +--- freecad-0.13.orig/debian/freecad.manpages ++++ freecad-0.13/debian/freecad.manpages +@@ -0,0 +1 @@ ++debian/freecad.1 +--- freecad-0.13.orig/debian/control ++++ freecad-0.13/debian/control +@@ -0,0 +1,67 @@ ++Source: freecad ++Section: science ++Priority: extra ++Maintainer: Debian Science Maintainers ++Uploaders: Teemu Ikonen , "Adam C. Powell, IV" , Anton Gladky ++Vcs-Browser: http://git.debian.org/?p=debian-science/packages/freecad.git ++Vcs-Git: git://git.debian.org/git/debian-science/packages/freecad.git ++Homepage: https://sourceforge.net/apps/mediawiki/free-cad/index.php?title=Main_Page ++Build-Depends: debhelper (>= 7.0.50~), cmake, ++ libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, ++ libboost-graph-dev, libboost-iostreams-dev, libboost-program-options-dev, ++ libboost-regex-dev, libboost-serialization-dev, libboost-signals-dev, ++ libboost-thread-dev, libboost-python-dev, python-dev, python-support, ++ libqt4-dev, libxt-dev, libxext-dev, libxmu-dev, libxi-dev, libx11-dev, ++ libcoin60-dev, libsoqt4-dev, libeigen3-dev, ++ zlib1g-dev, libxerces-c2-dev, libopencascade-foundation-dev, ++ libopencascade-modeling-dev, libopencascade-ocaf-dev, ++ libopencascade-visualization-dev, python-cxx-dev, libswscale-dev, ++ libzipios++-dev, swig, gfortran, f2c, libqtwebkit-dev, libspnav-dev, libfreetype6-dev ++Standards-Version: 3.9.1 ++ ++Package: freecad ++Architecture: any ++Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} ++Recommends: python-pivy, python ++Suggests: freecad-doc ++Description: Extensible Open Source CAx program (beta) ++ FreeCAD is an Open Source CAx RAD based on OpenCasCade, Qt and Python. ++ It features some key concepts like macro recording, workbenches, ability ++ to run as a server and dynamically loadable application extensions and ++ it is designed to be platform independent. ++ . ++ Currently, FreeCAD can import and display CAD models in IGES, STEP, and ++ BRep formats and meshes in STL, BMS, AST and Wavefront OBJ formats. ++ Editing and modeling features are currently somewhat limited. ++ ++Package: freecad-dev ++Architecture: any ++Section: libdevel ++Depends: ${shlibs:Depends}, ${misc:Depends}, freecad (= ${binary:Version}) ++Description: FreeCAD development files ++ FreeCAD is an Open Source CAx RAD based on OpenCasCade, Qt and Python. ++ It features some key concepts like macro recording, workbenches, ability ++ to run as a server and dynamically loadable application extensions and ++ it is designed to be platform independent. ++ For more details see http://sourceforge.net/projects/free-cad ++ . ++ This package contains headers and symlinks necessary to ++ develop modules for FreeCAD. ++ ++Package: freecad-doc ++Architecture: all ++Section: doc ++Depends: ${misc:Depends}, qt4-dev-tools ++Description: FreeCAD documentation ++ FreeCAD is an Open Source CAx RAD based on OpenCasCade, Qt and Python. ++ It features some key concepts like macro recording, workbenches, ability ++ to run as a server and dynamically loadable application extensions and ++ it is designed to be platform independent. ++ For more details see http://sourceforge.net/projects/free-cad ++ . ++ This package contains the FreeCAD documentation. ++ . ++ The documentation is provided in Qt's new help format; ++ the new help format version can be viewed in conjunction with the Qt Assistant ++ found in the qt4-dev-tools package. ++ +--- freecad-0.13.orig/debian/freecad.sharedmimeinfo ++++ freecad-0.13/debian/freecad.sharedmimeinfo +@@ -0,0 +1,8 @@ ++ ++ ++ ++ ++ FreeCAD document files ++ ++ ++ +--- freecad-0.13.orig/debian/compat ++++ freecad-0.13/debian/compat +@@ -0,0 +1 @@ ++7 +--- freecad-0.13.orig/debian/freecad.links ++++ freecad-0.13/debian/freecad.links +@@ -0,0 +1,3 @@ ++usr/lib/freecad/bin/FreeCAD usr/bin/freecad ++usr/lib/freecad/bin/FreeCADCmd usr/bin/freecadcmd ++usr/share/man/man1/freecad.1.gz usr/share/man/man1/freecadcmd.1.gz +--- freecad-0.13.orig/debian/freecad-doc.docs ++++ freecad-0.13/debian/freecad-doc.docs +@@ -0,0 +1 @@ ++debian/tmp/usr/doc/* +--- freecad-0.13.orig/debian/dirs ++++ freecad-0.13/debian/dirs +@@ -0,0 +1,8 @@ ++usr/share/icons/hicolor/16x16/apps ++usr/share/icons/hicolor/32x32/apps ++usr/share/icons/hicolor/48x48/apps ++usr/share/icons/hicolor/64x64/apps ++usr/share/icons/hicolor/scalable/apps ++usr/share/icons/hicolor/64x64/mimetypes ++usr/share/pixmaps ++ +--- freecad-0.13.orig/debian/freecad.desktop ++++ freecad-0.13/debian/freecad.desktop +@@ -0,0 +1,18 @@ ++[Desktop Entry] ++Version=1.0 ++Name=FreeCAD ++Name[de]=FreeCAD ++Comment=Feature based Parametric Modeler ++Comment[de]=Feature-basierter parametrischer Modellierer ++GenericName=CAD Application ++GenericName[de]=CAD-Anwendung ++Exec=/usr/bin/freecad %F ++Path=/usr/lib/freecad ++Terminal=false ++Type=Application ++Icon=freecad ++Categories=Graphics;Science;Engineering ++StartupNotify=true ++GenericName[de_DE]=Feature-basierter parametrischer Modellierer ++Comment[de_DE]=Feature-basierter parametrischer Modellierer ++MimeType=application/x-extension-fcstd +--- freecad-0.13.orig/debian/watch ++++ freecad-0.13/debian/watch +@@ -0,0 +1,2 @@ ++version=3 ++http://sf.net/free-cad/freecad_(.+)\.orig\.tar\.gz +--- freecad-0.13.orig/debian/freecad-doc.doc-base ++++ freecad-0.13/debian/freecad-doc.doc-base +@@ -0,0 +1,18 @@ ++Document: freecad-development-documentation ++Title: FreeCAD development documentation ++Author: FreeCAD developers ++Abstract: FreeCAD is a general purpose Open Source 3D ++ CAD/MCAD/CAx/CAE/PLM modeler, aimed directly at mechanical engineering ++ and product design but also fits in a wider range of uses around ++ engineering, such as architecture or other engineering specialties. ++ It is a feature-based parametric modeler with a modular software ++ architecture which makes it easy to provide additional functionality ++ without modifying the core system. ++Section: Science/Engineering ++ ++Format: HTML ++Index: /usr/share/doc/freecad/Start_Page.html ++Files: /usr/share/doc/freecad/*.q* ++ ++Format: PDF ++Files: /usr/share/doc/freecad/kr_16.pdf.gz /usr/share/doc/freecad/kr_210_2.pdf.gz /usr/share/doc/freecad/kr_500_2.pdf.gz +--- freecad-0.13.orig/debian/copyright ++++ freecad-0.13/debian/copyright +@@ -0,0 +1,275 @@ ++Format-Specification: http://dep.debian.net/deps/dep5/ ++Maintainer: Werner Mayer ++X-Packaged-By: Werner Mayer ++X-Packaged-Date: 2006-09-26_16:55:15+02:00 ++Source: http://sourceforge.net/projects/free-cad ++ ++Files: * ++Copyright: 2001-2009 Jürgen Riegel , ++ Werner Mayer ++License: LGPL-2+ ++ This package is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2 of the License, or (at your option) any later version. ++ . ++ This package is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ . ++ You should have received a copy of the GNU Lesser General Public ++ License along with this package; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, US ++ . ++ On Debian systems, the complete text of the GNU Lesser General Public ++ License version 2 can be found in `/usr/share/common-licenses/LGPL-2'. ++ ++Files: src/3rdParty/boost/numeric/bindings/* ++Copyright: 2002-2008 Kresimir Fresl, Karl Meerbergen, Toon Knapen, ++ Andreas Kloeckner, Jeremy Conlin, Thomas Klimpel, Fabien Dekeyser, ++ Quoc-Cuong Pham, Si-Lab b.v.b.a., Joerg Walter ++License: other-boost-1.0 ++ Boost Software License - Version 1.0 - August 17th, 2003 ++ . ++ Permission is hereby granted, free of charge, to any person or organization ++ obtaining a copy of the software and accompanying documentation covered by ++ this license (the "Software") to use, reproduce, display, distribute, ++ execute, and transmit the Software, and to prepare derivative works of the ++ Software, and to permit third-parties to whom the Software is furnished to ++ do so, all subject to the following: ++ . ++ The copyright notices in the Software and this entire statement, including ++ the above license grant, this restriction and the following disclaimer, ++ must be included in all copies of the Software, in whole or in part, and ++ all derivative works of the Software, unless such copies or derivative ++ works are solely in the form of machine-executable object code generated by ++ a source language processor. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ++ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ++ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++Files: src/3rdParty/boost/numeric/bindings/lapack/lapack.hpp, ++ src/3rdParty/boost/numeric/bindings/traits/std_valarray.hpp ++Copyright: 2003 Toon Knapen, Kresimir Fresl, Karl Meerbergen ++License: other ++ * Permission to copy, modify, use and distribute this software ++ * for any non-commercial or commercial purpose is granted provided ++ * that this license appear on all copies of the software source code. ++ * ++ * Authors assume no responsibility whatsoever for its use and makes ++ * no guarantees about its quality, correctness or reliability. ++ * ++ * KF acknowledges the support of the Faculty of Civil Engineering, ++ * University of Zagreb, Croatia. ++ ++Files: src/Base/Base64.* ++Copyright: 2004-2008 Rene Nyffenegger ++License: other ++ This source code is provided 'as-is', without any express or implied ++ warranty. In no event will the author be held liable for any damages ++ arising from the use of this software. ++ . ++ Permission is granted to anyone to use this software for any purpose, ++ including commercial applications, and to alter it and redistribute it ++ freely, subject to the following restrictions: ++ . ++ 1. The origin of this source code must not be misrepresented; you must not ++ claim that you wrote the original source code. If you use this source code ++ in a product, an acknowledgment in the product documentation would be ++ appreciated but is not required. ++ . ++ 2. Altered source versions must be plainly marked as such, and must not be ++ misrepresented as being the original source code. ++ . ++ 3. This notice may not be removed or altered from any source distribution. ++ ++Files: src/Base/fdstream.hpp ++Copyright: 2001 Nicolai M. Josuttis ++License: other ++ Permission to copy, use, modify, sell and distribute this software ++ is granted provided this copyright notice appears in all copies. ++ This software is provided "as is" without express or implied ++ warranty, and with no claim as to its suitability for any purpose. ++ ++Files: src/Base/gzstream.* ++Copyright: 2001 Deepak Bandyopadhyay, Lutz Kettner ++License: LGPL-2.1+ ++ ++Files: src/Base/PyTools.* ++Copyright: 1996-2000 Mark Lutz, and O'Reilly and Associates. ++License: other ++ Permission to use, copy, modify, and distribute this software ++ for any purpose and without fee is hereby granted. This software ++ is provided on an as is basis, without warranties of any kind. ++ ++Files: src/Doc/Start_Page.html ++Copyright: 2004-2009 UnaMesa Association ++License: BSD ++ Copyright (c) UnaMesa Association 2004-2009 ++ . ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions are ++ met: ++ . ++ Redistributions of source code must retain the above copyright notice, ++ this list of conditions and the following disclaimer. ++ . ++ Redistributions in binary form must reproduce the above copyright notice, ++ this list of conditions and the following disclaimer in the documentation ++ and/or other materials provided with the distribution. ++ . ++ Neither the name of the UnaMesa Association nor the names of its ++ contributors may be used to endorse or promote products derived from this ++ software without specific prior written permission. ++ . ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS ++ IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++Files: src/Doc/Start_Page.html ++Copyright: 2009 John Resig ++License: GPL-2 or MIT ++ On Debian systems, the complete text of the GNU General Public License ++ version 2 can be found in '/usr/share/common-licenses/GPL-2'. ++ ++License: MIT ++ Permission is hereby granted, free of charge, to any person obtaining ++ a copy of this software and associated documentation files (the ++ "Software"), to deal in the Software without restriction, including ++ without limitation the rights to use, copy, modify, merge, publish, ++ distribute, sublicense, and/or sell copies of the Software, and to ++ permit persons to whom the Software is furnished to do so, subject to ++ the following conditions: ++ . ++ The above copyright notice and this permission notice shall be ++ included in all copies or substantial portions of the Software. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ ++Files: src/Doc/Start_Page.html ++Copyright: 2009 The Dojo Foundation, http://sizzlejs.com/ ++License: GPL-2 or MIT or BSD ++ ++Files: src/Gui/iisTaskPanel/src/* ++Copyright: http://www.ii-system.com ++License: LGPL ++ ++Files: src/Mod/Draft/* ++Copyright: Yorik van Havre, Werner Mayer, Martin Burbaum ++License: GPL-2+ ++ ++Files: src/Mod/Draft/draftlibs/dxf* ++Copyright: 2005-2008 Ed Blake, Remigiusz Fiedler, Stani Michiels ++License: GPL-2+ ++ ++Files: src/Mod/Draft/WorkingPlane.py ++Copyright: 2009-2010 Ken Cline ++License: GPL-2+ ++ ++Files: src/Base/BoundBox.h, src/Base/Swap.*, src/Base/Vector3D.*, ++ src/Base/ViewProj.h, src/Base/Matrix.*, src/Base/Tools2D.*, ++ src/Mod/Mesh/App/Core/* ++Copyright: 2005 Imetric 3D GmbH ++License: LGPL-2+ ++ ++Files: src/Mod/Mesh/App/Core/Projection.cpp, src/Mod/Mesh/App/Core/Projection.h, ++ src/Mod/Mesh/App/Core/Triangulation.cpp, src/Mod/Mesh/App/Core/Triangulation.h ++Copyright: 2005 Werner Mayer ++License: LGPL-2+ ++ ++Files: src/Mod/Mesh/App/Core/Builder.h, src/Mod/Mesh/App/Core/SetOperations.* ++Copyright: 2005 Berthold Grupp ++License: LGPL-2+ ++ ++Files: src/Mod/Mesh/BuildRegularGeoms.py ++Copyright: 2005 Berthold Grupp ++License: LGPL ++ ++Files: src/Mod/Image/App/ImageBase.*, src/Mod/Image/Gui/GLImageBox.*, ++ src/Mod/Image/Gui/ImageView.*, src/Mod/Image/Gui/XpmImages.h ++Copyright: 2004 Imetric 3D GmbH ++License: LGPL-2+ ++ ++Files: src/Mod/Mesh/App/Core/tritritest.h ++Copyright: 1997 Tomas Moller ++License: other ++ tritritest.h has no licensing information, but Tomas Moller replied ++ the following, when asked about it: ++ . ++ The code is is free to use for anyone and any projects, but I give no ++ warranties. ++ ++Files: src/Mod/Mesh/App/WildMagic4/* ++Copyright: 1998-2007 David Eberly http://www.geometrictools.com ++License: LGPL-2.1+ ++ ++Files: src/Mod/Part/App/edgecluster.* ++Copyright: 2010 Joachim Zettler ++License: LGPL-2+ ++ ++Files: src/Mod/Raytracing/App/resources/* ++Copyright: 2005 Georg Wiora , ++ Juergen Riegel ++License: LGPL-2+ ++ ++Files: src/Mod/Sketcher/App/sketchflat/* ++Copyright: 2008 Jonathan Westhues ++License: GPL-3+ ++ On Debian systems, the complete text of the GNU General Public License ++ version 3 can be found in '/usr/share/common-licenses/GPL-3'. ++ ++Files: src/Mod/Sketcher/App/sketchsolve_cp/* ++Copyright: 2009 Jonathan George ++License: BSD ++ ++Files: src/Mod/Test/unittestgui.py ++Copyright: 1999-2001 Steve Purcell ++License: PSF ++ This module is free software, and you may redistribute it and/or modify ++ it under the same terms as Python itself, so long as this copyright message ++ and disclaimer are retained in their original form. ++ . ++ IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, ++ SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF ++ THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH ++ DAMAGE. ++ . ++ THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT ++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ++ PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, ++ AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, ++ SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ++ ++Files: src/Mod/Part/MakeBottle.py, src/Tools/* ++Copyright: 2002-2008 Jürgen Riegel , ++ Werner Mayer ++License: GPL-2+ ++ ++Files: src/Tools/generateBase/generateDS.py ++Copyright: 2003 Dave Kuhlman ++License: MIT ++ ++Files: debian/* ++Copyright: 2007-2009 Werner Mayer , ++ Teemu Ikonen ++License: LGPL-2+ +--- freecad-0.13.orig/debian/source/format ++++ freecad-0.13/debian/source/format +@@ -0,0 +1 @@ ++1.0 +--- freecad-0.13.orig/debian/source/lintian-overrides ++++ freecad-0.13/debian/source/lintian-overrides +@@ -0,0 +1,3 @@ ++# Lintian thinks uploader Adam Powell's name violates policy ++freecad source: uploader-address-missing "Adam C. Powell ++freecad source: uploader-not-full-name IV" +--- freecad-0.13.orig/debian/mime/freecad-thumbnailer ++++ freecad-0.13/debian/mime/freecad-thumbnailer +@@ -0,0 +1,37 @@ ++#!/usr/bin/python ++ ++import sys, zipfile ++import getopt ++from urlparse import urlparse ++from urlparse import unquote ++ ++opt,par = getopt.getopt(sys.argv[1:],'-s:') ++uri = urlparse(par[0]) ++if uri.scheme != "file": ++ sys.exit(1) ++ ++inpfile = unquote(uri.path) ++outfile = par[1] ++ ++try: ++ zfile=zipfile.ZipFile(inpfile) ++ files=zfile.namelist() ++ # check for meta-file if it's really a FreeCAD document ++ if files[0] != "Document.xml": ++ sys.exit(1) ++ ++ image="thumbnails/Thumbnail.png" ++ if image in files: ++ image=zfile.read(image) ++ else: ++ #freecad=open("/usr/share/freecad/freecad-doc.png") ++ #image=freecad.read() ++ sys.exit(1) ++ ++ thumb=open(outfile,"wb") ++ thumb.write(image) ++ thumb.close() ++ ++except: ++ sys.exit(1) ++ +--- freecad-0.13.orig/debian/mime/freecad.thumbnailer ++++ freecad-0.13/debian/mime/freecad.thumbnailer +@@ -0,0 +1,4 @@ ++[Thumbnailer Entry] ++TryExec=/usr/bin/freecad-thumbnailer ++Exec=/usr/bin/freecad-thumbnailer -s %s %u %o ++MimeType=application/x-extension-fcstd; +--- freecad-0.13.orig/debian/mime/freecad-thumbnailer.1 ++++ freecad-0.13/debian/mime/freecad-thumbnailer.1 +@@ -0,0 +1,20 @@ ++.TH FREECAD 1 "August 04, 2008" freecad "Linux User's Manual" ++.SH NAME ++freecad-thumbnailer \- A thumbnailer for FreeCAD project files ++.SH SYNOPSIS ++\fBfreecad-thumbnailer\fP [ -s \fIsize\fP ] \fIinput file\fP \fIoutput file\fP ++.SH DESCRIPTION ++\fBfreecad-thumbnailer\fP ++is a Python script that extracts an embedded thumbnail from a FreeCAD project file. ++If no thumbnail is embedded then nothing happens. According to the specification of ++freedesktop.org the thumbnail image is a PNG file. The required \fBinput file\fP argument ++specifies the project file where the thumbnail should be extracted from and the ++\fBoutput file\fP specifies the file where the thumbnail should be written to. ++.SH OPTIONS ++.TP ++\fB-s \fIsize\fR ++Specify the size of the image in pixel. ++.SH "SEE ALSO" ++freecad(1), freecadcmd(1) ++.SH AUTHOR ++This manual page was written by Werner Mayer. +--- freecad-0.13.orig/debian/mime/freecad.schemas ++++ freecad-0.13/debian/mime/freecad.schemas +@@ -0,0 +1,30 @@ ++ ++ ++ ++ ++ /schemas/desktop/gnome/thumbnailers/application@x-extension-fcstd/enable ++ /desktop/gnome/thumbnailers/application@x-extension-fcstd/enable ++ freecad ++ bool ++ true ++ ++ Enable thumbnailing of FreeCAD documents. ++ Enable thumbnailing of FreeCAD documents. ++ ++ ++ ++ ++ ++ /schemas/desktop/gnome/thumbnailers/application@x-extension-fcstd/command ++ /desktop/gnome/thumbnailers/application@x-extension-fcstd/command ++ freecad ++ string ++ /usr/bin/freecad-thumbnailer -s %s %u %o ++ ++ Thumbnail command for documents from FreeCAD. ++ Valid command plus arguments for freecad-thumbnailer. ++ ++ ++ ++ ++ diff --git a/package/debian/dirs b/package/debian/dirs new file mode 100644 index 000000000..4efc3d33b --- /dev/null +++ b/package/debian/dirs @@ -0,0 +1,8 @@ +usr/share/icons/hicolor/16x16/apps +usr/share/icons/hicolor/32x32/apps +usr/share/icons/hicolor/48x48/apps +usr/share/icons/hicolor/64x64/apps +usr/share/icons/hicolor/scalable/apps +usr/share/icons/hicolor/64x64/mimetypes +usr/share/pixmaps + diff --git a/package/debian/fcstd-thumbnailer.desktop b/package/debian/fcstd-thumbnailer.desktop deleted file mode 100644 index 13f982f3f..000000000 --- a/package/debian/fcstd-thumbnailer.desktop +++ /dev/null @@ -1,9 +0,0 @@ - -[Desktop Entry] -Version=1.0 -Encoding=UTF-8 -Type=X-Thumbnailer -Name=FreeCAD Thumbnailer -MimeType=application/x-extension-fcstd; -X-Thumbnailer-Exec=/usr/bin/freecad-thumbnailer %u %o %s -GenericName=FreeCADThumbnailer diff --git a/package/debian/freecad-doc.docs b/package/debian/freecad-doc.docs new file mode 100644 index 000000000..27e5cb820 --- /dev/null +++ b/package/debian/freecad-doc.docs @@ -0,0 +1 @@ +debian/tmp/usr/doc/* diff --git a/package/debian/freecad.desktop b/package/debian/freecad.desktop index 7e0b9e53e..9ef688f95 100644 --- a/package/debian/freecad.desktop +++ b/package/debian/freecad.desktop @@ -10,7 +10,7 @@ Exec=/usr/bin/freecad %F Path=/usr/lib/freecad Terminal=false Type=Application -Icon=/usr/share/freecad/freecad.xpm +Icon=freecad Categories=Graphics;Science;Engineering StartupNotify=true GenericName[de_DE]=Feature-basierter parametrischer Modellierer diff --git a/package/debian/freecad.manpages b/package/debian/freecad.manpages new file mode 100644 index 000000000..4586a8594 --- /dev/null +++ b/package/debian/freecad.manpages @@ -0,0 +1 @@ +debian/freecad.1 diff --git a/package/debian/freecad.sharedmimeinfo b/package/debian/freecad.sharedmimeinfo index 91a2b10ed..6d5e93e7d 100644 --- a/package/debian/freecad.sharedmimeinfo +++ b/package/debian/freecad.sharedmimeinfo @@ -1,7 +1,7 @@ - + FreeCAD document files diff --git a/package/debian/freecad.thumbnailer b/package/debian/freecad.thumbnailer deleted file mode 100644 index 3f78b3821..000000000 --- a/package/debian/freecad.thumbnailer +++ /dev/null @@ -1,4 +0,0 @@ -[Thumbnailer Entry] -TryExec=freecad-thumbnailer -Exec=freecad-thumbnailer -s %s %u %o -MimeType=application/x-extension-fcstd; diff --git a/package/debian/menu b/package/debian/menu index c939246be..fcfa2169f 100644 --- a/package/debian/menu +++ b/package/debian/menu @@ -2,5 +2,5 @@ section="Applications/Science/Engineering"\ title="FreeCAD"\ command="/usr/bin/freecad"\ - icon="/usr/share/freecad/freecad.xpm" + icon="/usr/share/pixmaps/freecad.xpm" diff --git a/package/debian/mime/freecad-thumbnailer b/package/debian/mime/freecad-thumbnailer index a1081feb2..e93508e23 100755 --- a/package/debian/mime/freecad-thumbnailer +++ b/package/debian/mime/freecad-thumbnailer @@ -1,11 +1,16 @@ #!/usr/bin/python -import sys, zipfile, md5 +import sys, zipfile import getopt -import gnomevfs +from urlparse import urlparse +from urlparse import unquote opt,par = getopt.getopt(sys.argv[1:],'-s:') -inpfile = gnomevfs.get_local_path_from_uri(par[0]) +uri = urlparse(par[0]) +if uri.scheme != "file": + sys.exit(1) + +inpfile = unquote(uri.path) outfile = par[1] try: @@ -19,8 +24,9 @@ try: if image in files: image=zfile.read(image) else: - freecad=open("/usr/share/freecad/freecad-doc.png") - image=freecad.read() + #freecad=open("/usr/share/freecad/freecad-doc.png") + #image=freecad.read() + sys.exit(1) thumb=open(outfile,"wb") thumb.write(image) diff --git a/package/debian/mime/freecad.thumbnailer b/package/debian/mime/freecad.thumbnailer new file mode 100644 index 000000000..e54d9a7a1 --- /dev/null +++ b/package/debian/mime/freecad.thumbnailer @@ -0,0 +1,4 @@ +[Thumbnailer Entry] +TryExec=/usr/bin/freecad-thumbnailer +Exec=/usr/bin/freecad-thumbnailer -s %s %u %o +MimeType=application/x-extension-fcstd; diff --git a/package/debian/occ-with-libs.patch b/package/debian/occ-with-libs.patch deleted file mode 100644 index b32642cf7..000000000 --- a/package/debian/occ-with-libs.patch +++ /dev/null @@ -1,78 +0,0 @@ -Index: debian/rules -=================================================================== ---- debian/rules (revision 1523) -+++ debian/rules (working copy) -@@ -11,6 +11,10 @@ - - MODULES = Part Mesh Points Raytracing Image Drawing Test - -+# Preliminary only as long as no official debian package for -+# OpenCascade is available -+DESTOCCDIR=$(shell pwd)/debian/freecad/usr/lib/freecad/lib -+ - # These are used for cross-compiling and for saving the configure script - # from having to guess our platform (since we know it already) - DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -@@ -85,6 +89,33 @@ - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib/FreeCAD.so usr/lib/freecad/lib - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib/lib*.so.* usr/lib/freecad/lib - -+# Preliminary only as long as no official debian package for -+# OpenCascade is available -+ install -m777 /usr/lib/libTKernel-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKMath-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKG2d-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKG3d-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKGeomBase-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKBRep-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKGeomAlgo-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKTopAlgo-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKPrim-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKBO-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKBool-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKShHealing-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKXSBase-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKSTEPBase-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKSTEPAttr-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKSTEP209-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKSTEP-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKFillet-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKOffset-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKIGES-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKMesh-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKHLR-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libTKSTL-6.2.so $(DESTOCCDIR) -+ install -m777 /usr/lib/libSoQt4.so.20 $(DESTOCCDIR) -+ - # install the modules - $(foreach MODULE,$(MODULES), \ - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/$(MODULE)*.so usr/lib/freecad/Mod/$(MODULE); \ -Index: debian/shlibs.local -=================================================================== ---- debian/shlibs.local (revision 0) -+++ debian/shlibs.local (revision 0) -@@ -0,0 +1,24 @@ -+libTKernel 6.2 -+libTKMath 6.2 -+libTKG2d 6.2 -+libTKG3d 6.2 -+libTKGeomBase 6.2 -+libTKBRep 6.2 -+libTKGeomAlgo 6.2 -+libTKTopAlgo 6.2 -+libTKPrim 6.2 -+libTKBO 6.2 -+libTKBool 6.2 -+libTKShHealing 6.2 -+libTKXSBase 6.2 -+libTKSTEPAttr 6.2 -+libTKSTEPBase 6.2 -+libTKSTEP209 6.2 -+libTKSTEP 6.2 -+libTKFillet 6.2 -+libTKOffset 6.2 -+libTKIGES 6.2 -+libTKMesh 6.2 -+libTKHLR 6.2 -+libTKSTL 6.2 -+libSoQt4 20 diff --git a/package/debian/patch/debian_testing.patch b/package/debian/patch/debian_testing.patch deleted file mode 100644 index d49cc25ee..000000000 --- a/package/debian/patch/debian_testing.patch +++ /dev/null @@ -1,33 +0,0 @@ -Index: control -=================================================================== ---- control (revision 2213) -+++ control (working copy) -@@ -4,21 +4,21 @@ - Maintainer: Werner Mayer - Homepage: http://sourceforge.net/projects/free-cad - Build-Depends: debhelper (>= 5), autotools-dev, libc6-dev (>= 2.1.3), -- libstdc++6, libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, -- libboost-graph-dev, libboost-iostreams-dev, libboost-program-options-dev, -- libboost-regex-dev, libboost-serialization-dev, libboost-signals-dev, -- zlib1g-dev, libxerces27-dev | libxerces-c2-dev, -+ libstdc++6, libboost1.37-dev, libboost-date-time1.37-dev, libboost-filesystem1.37-dev, -+ libboost-graph1.37-dev, libboost-iostreams1.37-dev, libboost-program-options1.37-dev, -+ libboost-regex1.37-dev, libboost-serialization1.37-dev, libboost-signals1.37-dev, -+ zlib1g-dev, libxerces-c28 | libxerces-c2-dev, - libxt-dev, libxmu-dev, libxi-dev, libx11-dev, libxext-dev, -- libqt4-dev, libsoqt4-dev, libcoin40-dev, libgl1-mesa-dev, -+ libqt4-dev, libsoqt4-dev, libcoin60-dev, libgl1-mesa-dev, - python2.5-dev, python, python-central (>= 0.5.6), -- libgts-dev, libcv-dev, libopencascade-dev -+ libgts-dev, libcv-dev, libopencascade-foundation-dev, libopencascade-modeling-dev - Standards-Version: 3.7.3 - XS-Python-Version: current - - Package: freecad - Architecture: any - Section: science --Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python -+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python, python-pivy - XB-Python-Version: ${python:Versions} - Conflicts: freecad (<= 0.6.472-1) - Suggests: gnochm | kchmviewer | kchmviewer-nokde | xchm, python-opencv diff --git a/package/debian/patch/ubuntu_intrepid.patch b/package/debian/patch/ubuntu_intrepid.patch deleted file mode 100644 index 5cab88b9b..000000000 --- a/package/debian/patch/ubuntu_intrepid.patch +++ /dev/null @@ -1,25 +0,0 @@ -Index: debian/control -=================================================================== ---- debian/control (Revision 1881) -+++ debian/control (Arbeitskopie) -@@ -4,15 +4,15 @@ - Maintainer: Werner Mayer - Homepage: http://sourceforge.net/projects/free-cad - Build-Depends: debhelper (>= 5), autotools-dev, libc6-dev (>= 2.1.3), -- libstdc++6, libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, -- libboost-graph-dev, libboost-iostreams-dev, libboost-program-options-dev, -- libboost-regex-dev, libboost-serialization-dev, libboost-signals-dev, -- zlib1g-dev, libxerces27-dev | libxerces-c2-dev, -+ libstdc++6, libboost1.35-dev, libboost-date-time1.35-dev, libboost-filesystem1.35-dev, -+ libboost-graph1.35-dev, libboost-iostreams1.35-dev, libboost-program-options1.35-dev, -+ libboost-regex1.35-dev, libboost-serialization1.35-dev, libboost-signals1.35-dev, -+ libboost-system1.35-dev, zlib1g-dev, libxerces27-dev | libxerces-c2-dev, - libxt-dev, libxmu-dev, libxi-dev, libx11-dev, libxext-dev, - libqt4-dev, libsoqt4-dev, libcoin40-dev, libgl1-mesa-dev, - python2.5-dev, python, python-central (>= 0.5.6), - libgts-dev, libcv-dev, libopencascade-dev --Standards-Version: 3.7.3 -+Standards-Version: 3.8.0 - XS-Python-Version: current - - Package: freecad diff --git a/package/debian/patch/ubuntu_jaunty.patch b/package/debian/patch/ubuntu_jaunty.patch deleted file mode 100644 index 397655d87..000000000 --- a/package/debian/patch/ubuntu_jaunty.patch +++ /dev/null @@ -1,25 +0,0 @@ -Index: debian/control -=================================================================== ---- debian/control (Revision 1879) -+++ debian/control (Arbeitskopie) -@@ -4,15 +4,15 @@ - Maintainer: Werner Mayer - Homepage: http://sourceforge.net/projects/free-cad - Build-Depends: debhelper (>= 5), autotools-dev, libc6-dev (>= 2.1.3), -- libstdc++6, libboost-dev, libboost-date-time-dev, libboost-filesystem-dev, -- libboost-graph-dev, libboost-iostreams-dev, libboost-program-options-dev, -- libboost-regex-dev, libboost-serialization-dev, libboost-signals-dev, -- zlib1g-dev, libxerces27-dev | libxerces-c2-dev, -+ libstdc++6, libboost1.35-dev, libboost-date-time1.35-dev, libboost-filesystem1.35-dev, -+ libboost-graph1.35-dev, libboost-iostreams1.35-dev, libboost-program-options1.35-dev, -+ libboost-regex1.35-dev, libboost-serialization1.35-dev, libboost-signals1.35-dev, -+ libboost-system1.35-dev, zlib1g-dev, libxerces27-dev | libxerces-c2-dev, - libxt-dev, libxmu-dev, libxi-dev, libx11-dev, libxext-dev, - libqt4-dev, libsoqt4-dev, libcoin40-dev, libgl1-mesa-dev, - python2.5-dev, python, python-central (>= 0.5.6), - libgts-dev, libcv-dev, libopencascade-dev --Standards-Version: 3.7.3 -+Standards-Version: 3.8.0 - XS-Python-Version: current - - Package: freecad diff --git a/package/debian/rules b/package/debian/rules index 72ac81266..52bdb1356 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -4,7 +4,7 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -MODULES = Part Mesh MeshPart PartDesign Sketcher Points Raytracing Image Drawing ReverseEngineering Complete Fem Robot Import Inspection Arch +# For testing: fakeroot debian/rules binary # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) @@ -19,58 +19,32 @@ else CFLAGS += -O2 endif -patch-stamp: - touch $@ - -configure: autogen.sh patch-stamp +configure: dh_testdir - for autotools_mod_file in `find . -name Makefile.in` aclocal.m4 \ - configure m4/libtool.m4 m4/ltmain.sh m4/ltoptions.m4 \ - m4/ltversion.m4 m4/lt~obsolete.m4; do \ - cp -a $$autotools_mod_file $$autotools_mod_file.setaside; \ - done - chmod u+x autogen.sh - ./autogen.sh - -config.status: configure - dh_testdir - ./configure --with-occ-include=/usr/include/opencascade \ ---with-occ-lib=/usr/lib \ ---host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \ ---prefix=/usr/lib/freecad --mandir=/usr/share/man \ ---infodir=/usr/share/info --datadir=/usr/share/freecad \ ---includedir=/usr/include/freecad --docdir=/usr/share/doc/freecad \ -CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs" - touch src/Build/Version.h + cmake . \ +-DFREECAD_BUILD_DEBIAN=ON \ +-DCMAKE_INSTALL_PREFIX=/usr/lib/freecad \ +-DCMAKE_INSTALL_MANDIR=/usr/share/man \ +-DCMAKE_INSTALL_INFODIR=/usr/share/info \ +-DCMAKE_INSTALL_DATADIR=/usr/share/freecad \ +-DCMAKE_INSTALL_INCLUDEDIR=/usr/include/freecad \ +-DCMAKE_INSTALL_DOCDIR=/usr/share/doc/freecad build: build-stamp -build-stamp: config.status +build-stamp: configure dh_testdir $(MAKE) touch $@ clean: - mv src/Build/Version.h src/Build/Version.h.old dh clean - mv src/Build/Version.h.old src/Build/Version.h rm -f build-stamp find -name '*.pyc' | xargs rm -f find -name 'moc_*.cpp' | xargs rm -f - find -name '*.lo' | xargs rm -f - find -name '*.deps' | xargs rm -rf - find -name '*.libs' | xargs rm -rf - rm -f stamp-h1 config.log libtool 71 - if [ -e Makefile.in.setaside ]; then \ - for autotools_mod_file in `find . -name Makefile.in` aclocal.m4 \ - configure m4/libtool.m4 m4/ltmain.sh m4/ltoptions.m4 \ - m4/ltversion.m4 m4/lt~obsolete.m4; do \ - mv -f $$autotools_mod_file.setaside $$autotools_mod_file; \ - done; fi - dh clean - rm -f patch-stamp - #quilt pop -a - #rm -rf .pc/ + find -name 'ui_*.h' | xargs rm -f + find -name 'CMakeFiles' | xargs rm -rf + rm -f stamp-h1 install: build install-stamp install-stamp: @@ -78,44 +52,34 @@ install-stamp: dh_testroot dh_prep dh_installdirs - $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp/freecad - # Remove testing modules we don't want to have in the deb - rm -rf debian/tmp/freecad/usr/lib/freecad/Mod/_TEMPLATE_ - rm -rf debian/tmp/freecad/usr/lib/freecad/Mod/TemplatePyMod + $(MAKE) install/fast DESTDIR=$(CURDIR)/debian/tmp/freecad # install the core system dh_install -pfreecad debian/tmp/freecad/usr/share/freecad/* usr/share/freecad/ - #dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/share usr/lib/freecad + # Desktop icons + dh_install -pfreecad debian/tmp/freecad/usr/share/freecad/freecad.xpm usr/share/pixmaps + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-16.png debian/freecad/usr/share/icons/hicolor/16x16/apps/freecad.png + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-32.png debian/freecad/usr/share/icons/hicolor/32x32/apps/freecad.png + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-32.png debian/freecad/usr/share/icons/hicolor/48x48/apps/freecad.png + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-icon-64.png debian/freecad/usr/share/icons/hicolor/64x64/apps/freecad.png + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad.svg debian/freecad/usr/share/icons/hicolor/scalable/apps/freecad.svg + install -m 644 debian/tmp/freecad/usr/share/freecad/freecad-doc.png debian/freecad/usr/share/icons/hicolor/64x64/mimetypes/application-x-extension-fcstd.png + dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/bin usr/lib/freecad - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib/FreeCAD.so usr/lib/freecad/lib - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib/lib*.so.* usr/lib/freecad/lib + dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/lib usr/lib/freecad + dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod usr/lib/freecad dh_install debian/freecad.desktop usr/share/applications - dh_installman debian/freecad.1 + dh_installman debian/freecad.1 debian/mime/freecad-thumbnailer.1 dh_installchangelogs ChangeLog.txt - # install the modules - $(foreach MODULE,$(MODULES), \ - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/$(MODULE)*.so usr/lib/freecad/Mod/$(MODULE); \ - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/lib*.so.* usr/lib/freecad/Mod/$(MODULE); \ - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/*.py usr/lib/freecad/Mod/$(MODULE);) - # special treating of PartDesign module - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/PartDesign/Scripts/*.py usr/lib/freecad/Mod/PartDesign/Scripts;) - # special treating of Draft module - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Draft/*.py usr/lib/freecad/Mod/Draft - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Draft/draftlibs/*.py usr/lib/freecad/Mod/Draft/draftlibs - # special treating of Test module - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Test/lib*.so.* usr/lib/freecad/Mod/Test - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Test/*.py usr/lib/freecad/Mod/Test - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Test/QtUnitGui.so usr/lib/freecad/Mod/Test - - dh_install -pfreecad-dev debian/tmp/freecad/usr/include/* usr/include - dh_install -pfreecad-dev debian/tmp/freecad/usr/lib/freecad/lib/lib*.so usr/lib/freecad/lib - $(foreach MODULE,$(MODULES), \ - dh_install -pfreecad-dev debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/lib*.la usr/lib/freecad/Mod/$(MODULE); \ - dh_install -pfreecad-dev debian/tmp/freecad/usr/lib/freecad/Mod/$(MODULE)/lib*.so usr/lib/freecad/Mod/$(MODULE);) - # special treating of Arch module - dh_install -pfreecad debian/tmp/freecad/usr/lib/freecad/Mod/Arch/*.py usr/lib/freecad/Mod/Arch - + # install the headers + #dh_install -pfreecad-dev debian/tmp/freecad/usr/include/* usr/include # install the help system dh_install -pfreecad-doc debian/tmp/freecad/usr/share/doc/* usr/share/doc/ + # install MIME stuff + dh_install debian/mime/freecad-thumbnailer usr/bin + dh_install debian/mime/freecad.thumbnailer usr/share/thumbnailers + dh_install debian/mime/freecad.schemas etc/gconf/schemas + dh_gconf -pfreecad + dh_installmime touch install-stamp override_dh_compress: diff --git a/package/debian/scripts/get_git_orig_src.sh b/package/debian/scripts/get_git_orig_src.sh new file mode 100644 index 000000000..f58e8d9d8 --- /dev/null +++ b/package/debian/scripts/get_git_orig_src.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# The script creates a tar.xz tarball from git-repository of freecad-project +# ./get_orig_src.sh commitID - creates a tarball of specified commit +# ./get_orig_src.sh - creates a tarball of the latest version +# Packages, that needs to be installed to use the script: +# atool, git-core + +set -e + +git clone git://free-cad.git.sourceforge.net/gitroot/free-cad/free-cad git_temp_packaging + +cd git_temp_packaging + +if [ $1 ] +then + echo 'Checking out the revision ' $1 + git checkout -b newvers $1 +else + echo 'Using the latest revision' +fi + +GIT_CMT_COUNT=$(git rev-list HEAD | wc -l) + +DEB_VER=0.13.$GIT_CMT_COUNT-dfsg +FOLDER_NAME=freecad-$DEB_VER +TARBALL_NAME=freecad_$DEB_VER.orig.tar.xz + +echo $DEB_VER +echo $FOLDER_NAME +echo $TARBALL_NAME + +python src/Tools/SubWCRev.py + +cd .. + +rm -fr $FOLDER_NAME + +mv git_temp_packaging $FOLDER_NAME +rm -rf $FOLDER_NAME/.git +rm -rf $FOLDER_NAME/src/3rdParty/CxImage +rm -rf $FOLDER_NAME/src/3rdParty/Pivy +rm -rf $FOLDER_NAME/src/3rdParty/Pivy-0.5 + +tar Jcvf $TARBALL_NAME $FOLDER_NAME + +rm -fr $FOLDER_NAME diff --git a/package/debian/shlibs.local b/package/debian/shlibs.local deleted file mode 100644 index e7cacaa24..000000000 --- a/package/debian/shlibs.local +++ /dev/null @@ -1,2 +0,0 @@ -libGL 1 libgl1-mesa-glx (>= 7.7.1-1) -libSoQt4 20 libsoqt4-20 (>= 1.4.2~svn20090224) diff --git a/package/debian/source/format b/package/debian/source/format index 163aaf8d8..d3827e75a 100644 --- a/package/debian/source/format +++ b/package/debian/source/format @@ -1 +1 @@ -3.0 (quilt) +1.0 diff --git a/package/debian/source/lintian-overrides b/package/debian/source/lintian-overrides index a7a5da0c9..0a52f43ba 100644 --- a/package/debian/source/lintian-overrides +++ b/package/debian/source/lintian-overrides @@ -1,3 +1,3 @@ -# Lintian thinks uploader Adam Powell's name violates policy -freecad source: uploader-address-missing "Adam C. Powell -freecad source: uploader-not-full-name IV" +# Lintian thinks uploader Adam Powell's name violates policy +freecad source: uploader-address-missing "Adam C. Powell +freecad source: uploader-not-full-name IV" diff --git a/package/makedebian.sh b/package/makedebian.sh index 6b8f15811..e1809b34d 100755 --- a/package/makedebian.sh +++ b/package/makedebian.sh @@ -16,68 +16,36 @@ # http://www.grymoire.com/Unix/Sed.html # global settings -REV_FILE=./revision.m4 TMP_PATH=/tmp MAJ=0 -MIN=12 -ALIAS="Vulcan" +MIN=13 # go to root directory CUR_DIR=$PWD verz=`dirname $(readlink -f ${0})` cd $verz && cd .. -# let's import OLD_REV (if there) -if [ -f ./.last_revision ]; then - . ./.last_revision -else - OLD_REV=0 -fi - -if svn --xml info >/dev/null 2>&1; then - REV=`svn --xml info | tr -d '\r\n' | sed -e 's/.*.*/\1/'` - LCD=`svn --xml info | tr -d '\r\n' | sed -e 's/.*\([0-9\-]*\)\T\([0-9\:]*\)\..*<\/date>.*<\/commit>.*/\1 \2/'` - URL=`svn --xml info | tr -d '\r\n' | sed -e 's/.*\(.*\)<\/url>.*/\1/'` -elif svn --version --quiet >/dev/null 2>&1; then - REV=`svn info | grep "^Revision:" | cut -d" " -f2` - LCD=`svn info | grep "^Last Changed Date:" | cut -d" " -f4,5` - URL=`svn info | grep "^URL:" | cut -d" " -f2` +# http://blog.marcingil.com/2011/11/creating-build-numbers-using-git-commits/ +if git log -1 >/dev/null 2>&1; then + REV=`git rev-list HEAD | wc -l | sed -e 's/ *//g' | xargs -n1 printf %04d` else REV=0 - LCD="" - URL="" fi -if [ "x$REV" != "x$OLD_REV" -o ! -r $REV_FILE ]; then - echo "m4_define([FREECAD_MAJOR], $MAJ)" > $REV_FILE - echo "m4_define([FREECAD_MINOR], $MIN)" >> $REV_FILE - echo "m4_define([FREECAD_MICRO], $REV)" >> $REV_FILE - - #echo "#define FCVersionMajor \"$MAJ\"" > src/Build/Version.h - #echo "#define FCVersionMinor \"$MIN\"" >> src/Build/Version.h - #echo "#define FCVersionName \"$ALIAS\"" >> src/Build/Version.h - #echo "#define FCRevision \"$REV\"" >> src/Build/Version.h - #echo "#define FCRepositoryURL \"$URL\"" >> src/Build/Version.h - #echo "#define FCCurrentDateT \"$LCD\"\n" >> src/Build/Version.h - touch src/Build/Version.h.in -fi - -echo "OLD_REV=$REV" > ./.last_revision - SRC_DIR=$PWD # Prepare source tarball and unpack it in build directory cd $CUR_DIR -make dist +make dist-git +cd $verz && cd .. rm -rf $TMP_PATH/freecad-$REV mkdir $TMP_PATH/freecad-$REV -mv FreeCAD-$MAJ.$MIN.$REV.tar.gz $TMP_PATH/freecad-$REV/freecad_$MAJ.$MIN.$REV.orig.tar.gz +mv freecad-$MAJ.$MIN.$REV.tar.gz $TMP_PATH/freecad-$REV/freecad_$MAJ.$MIN.$REV.orig.tar.gz cd $TMP_PATH/freecad-$REV tar -xzf freecad_$MAJ.$MIN.$REV.orig.tar.gz -mv FreeCAD-$MAJ.$MIN.$REV freecad-$MAJ.$MIN.$REV cd freecad-$MAJ.$MIN.$REV -rm -rf src/CXX -rm -rf src/zipios++ +#rm -rf src/CXX +#rm -rf src/zipios++ # Prepare debian folder and set the revision number in debian/changelog # for package versioning diff --git a/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_RadialPrism_3D.cpp b/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_RadialPrism_3D.cpp index 133b68160..fb6be0b1e 100644 --- a/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_RadialPrism_3D.cpp +++ b/src/3rdParty/salomesmesh/src/StdMeshers/StdMeshers_RadialPrism_3D.cpp @@ -42,9 +42,13 @@ #include "utilities.h" +#include #include #include #include +#if OCC_VERSION_HEX >= 0x060600 +#include +#endif #include #include #include @@ -159,7 +163,11 @@ bool StdMeshers_RadialPrism_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& a // get 2 shells TopoDS_Solid solid = TopoDS::Solid( aShape ); +#if OCC_VERSION_HEX >= 0x060600 + TopoDS_Shell outerShell = BRepClass3d::OuterShell( solid ); +#else TopoDS_Shell outerShell = BRepTools::OuterShell( solid ); +#endif TopoDS_Shape innerShell; int nbShells = 0; for ( TopoDS_Iterator It (solid); It.More(); It.Next(), ++nbShells ) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 113200e85..f1b526f56 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1008,7 +1008,10 @@ void Application::initTypes(void) App ::PropertyPercent ::init(); App ::PropertyEnumeration ::init(); App ::PropertyIntegerList ::init(); + App ::PropertyIntegerSet ::init(); + App ::PropertyMap ::init(); App ::PropertyString ::init(); + App ::PropertyUUID ::init(); App ::PropertyFont ::init(); App ::PropertyStringList ::init(); App ::PropertyLink ::init(); @@ -1211,9 +1214,10 @@ void Application::processCmdLineFiles(void) Base::Interpreter().runFile(File.filePath().c_str(), true); } else if (File.hasExtension("py")) { - try{ + try { Base::Interpreter().loadModule(File.fileNamePure().c_str()); - }catch(PyException){ + } + catch(const PyException&) { // if module load not work, just try run the script (run in __main__) Base::Interpreter().runFile(File.filePath().c_str(),true); } diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 5ccde012e..ee92e46da 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -64,6 +64,10 @@ recompute path. Also enables more complicated dependencies beyond trees. #include #include #include +#include + +#include +#include #include "Document.h" @@ -433,8 +437,41 @@ unsigned int Document::getMaxUndoStackSize(void)const void Document::onChanged(const Property* prop) { // the Name property is a label for display purposes - if (prop == &Label) + if (prop == &Label) { App::GetApplication().signalRelabelDocument(*this); + } + else if (prop == &Uid) { + std::string new_dir = getTransientDirectoryName(this->Uid.getValueStr(),this->FileName.getStrValue()); + std::string old_dir = this->TransientDir.getStrValue(); + Base::FileInfo TransDirNew(new_dir); + Base::FileInfo TransDirOld(old_dir); + // this directory should not exist + if (!TransDirNew.exists()) { + if (TransDirOld.exists()) { + if (!TransDirOld.renameFile(new_dir.c_str())) + Base::Console().Warning("Failed to rename '%s' to '%s'\n", old_dir.c_str(), new_dir.c_str()); + else + this->TransientDir.setValue(new_dir); + } + else { + if (!TransDirNew.createDirectory()) + Base::Console().Warning("Failed to create '%s'\n", new_dir.c_str()); + else + this->TransientDir.setValue(new_dir); + } + } + // when reloading an existing document the transient directory doesn't change + // so we must avoid to generate a new uuid + else if (TransDirNew.filePath() != TransDirOld.filePath()) { + // make sure that the uuid is unique + std::string uuid = this->Uid.getValueStr(); + Base::Uuid id; + Base::Console().Warning("Document with the UUID '%s' already exists, change to '%s'\n", + uuid.c_str(), id.getValue().c_str()); + // recursive call of onChanged() + this->Uid.setValue(id); + } + } } void Document::onBeforeChangeProperty(const DocumentObject *Who, const Property *What) @@ -525,7 +562,7 @@ Document::Document(void) #endif ADD_PROPERTY_TYPE(Label,("Unnamed"),0,Prop_None,"The name of the document"); - ADD_PROPERTY_TYPE(FileName,(""),0,Prop_None,"The path to the file where the document is saved to"); + ADD_PROPERTY_TYPE(FileName,(""),0,Prop_ReadOnly,"The path to the file where the document is saved to"); ADD_PROPERTY_TYPE(CreatedBy,(""),0,Prop_None,"The creator of the document"); ADD_PROPERTY_TYPE(CreationDate,(Base::TimeInfo::currentDateTimeString()),0,Prop_ReadOnly,"Date of creation"); ADD_PROPERTY_TYPE(LastModifiedBy,(""),0,Prop_None,0); @@ -537,19 +574,16 @@ Document::Document(void) // create the uuid for the document Base::Uuid id; ADD_PROPERTY_TYPE(Id,(""),0,Prop_None,"ID of the document"); - ADD_PROPERTY_TYPE(Uid,(id),0,Prop_None,"UUID of the document"); + ADD_PROPERTY_TYPE(Uid,(id),0,Prop_ReadOnly,"UUID of the document"); // license stuff ADD_PROPERTY_TYPE(License,("CC-BY 3.0"),0,Prop_None,"License string of the Item"); ADD_PROPERTY_TYPE(LicenseURL,("http://creativecommons.org/licenses/by/3.0/"),0,Prop_None,"URL to the license text/contract"); - // create transient directory - std::string basePath = Base::FileInfo::getTempPath() + GetApplication().getExecutableName(); - Base::FileInfo TransDir(basePath + "_Doc_" + id.getValue()); - if (!TransDir.exists()) - TransDir.createDirectory(); - ADD_PROPERTY_TYPE(TransientDir,(TransDir.filePath().c_str()),0,Prop_Transient, + // this creates and sets 'TransientDir' in onChanged() + ADD_PROPERTY_TYPE(TransientDir,(""),0,PropertyType(Prop_Transient|Prop_ReadOnly), "Transient directory, where the files live while the document is open"); + Uid.touch(); } Document::~Document() @@ -574,7 +608,7 @@ Document::~Document() // Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed // Python object or not. In the constructor we forced the wrapper to own the object so we need // not to dec'ref the Python object any more. - // But we must still invalidate the Python object because it need not to be + // But we must still invalidate the Python object because it doesn't need to be // destructed right now because the interpreter can own several references to it. Base::PyObjectBase* doc = (Base::PyObjectBase*)DocumentPythonObject.ptr(); // Call before decrementing the reference counter, otherwise a heap error can occur @@ -586,6 +620,19 @@ Document::~Document() delete d; } +std::string Document::getTransientDirectoryName(const std::string& uuid, const std::string& filename) const +{ + // Create a directory name of the form: {ExeName}_Doc_{UUID}_{HASH}_{PID} + std::stringstream s; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(filename.c_str(), filename.size()); + s << Base::FileInfo::getTempPath() << GetApplication().getExecutableName() + << "_Doc_" << uuid + << "_" << hash.result().toHex().left(6).constData() + << "_" << QCoreApplication::applicationPid(); + return s.str(); +} + //-------------------------------------------------------------------------- // Exported functions //-------------------------------------------------------------------------- @@ -623,12 +670,7 @@ void Document::Restore(Base::XMLReader &reader) std::string FilePath = FileName.getValue(); std::string DocLabel = Label.getValue(); - // remove previous Transient directory - Base::FileInfo TransDir(TransientDir.getValue()); - TransDir.deleteDirectoryRecursive(); - - - // read the Document Properties + // read the Document Properties, when reading in Uid the transient directory gets renamed automatically PropertyContainer::Restore(reader); // We must restore the correct 'FileName' property again because the stored @@ -636,14 +678,6 @@ void Document::Restore(Base::XMLReader &reader) FileName.setValue(FilePath.c_str()); Label.setValue(DocLabel.c_str()); - // create new transient directory - std::string basePath = Base::FileInfo::getTempPath() + GetApplication().getExecutableName(); - Base::FileInfo TransDirNew(basePath + "_Doc_" + Uid.getValueStr()); - if(!TransDirNew.exists()) - TransDirNew.createDirectory(); - TransientDir.setValue(TransDirNew.filePath()); - - // SchemeVersion "2" if ( scheme == 2 ) { // read the feature types @@ -769,8 +803,8 @@ Document::readObjects(Base::XMLReader& reader) reader.addName(name.c_str(), obj->getNameInDocument()); } } - catch (Base::Exception&) { - Base::Console().Message("Cannot create object '%s'\n", name.c_str()); + catch (const Base::Exception& e) { + Base::Console().Error("Cannot create object '%s': (%s)\n", name.c_str(), e.what()); } } reader.readEndElement("Objects"); @@ -857,6 +891,18 @@ void Document::exportGraphviz(std::ostream& out) boost::write_graphviz(out, DepList, boost::make_label_writer(&(names[0]))); } +bool Document::saveAs(const char* file) +{ + Base::FileInfo fi(file); + if (this->FileName.getStrValue() != file) { + this->FileName.setValue(file); + this->Label.setValue(fi.fileNamePure()); + this->Uid.touch(); // this forces a rename of the transient directory + } + + return save(); +} + // Save the document under the name it has been opened bool Document::save (void) { @@ -1090,6 +1136,58 @@ std::vector Document::getInList(const DocumentObject* me) return result; } +std::vector +Document::getDependencyList(const std::vector& objs) const +{ + DependencyList DepList; + std::map ObjectMap; + std::map VertexMap; + + // Filling up the adjacency List + for (std::map::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) { + // add the object as Vertex and remember the index + Vertex v = add_vertex(DepList); + ObjectMap[It->second] = v; + VertexMap[v] = It->second; + } + // add the edges + for (std::map::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) { + std::vector OutList = It->second->getOutList(); + for (std::vector::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) { + if (*It2) + add_edge(ObjectMap[It->second],ObjectMap[*It2],DepList); + } + } + + std::list make_order; + DependencyList::out_edge_iterator j, jend; + + try { + // this sort gives the execute + boost::topological_sort(DepList, std::front_inserter(make_order)); + } + catch (const std::exception&) { + return std::vector(); + } + + //std::vector out; + boost::unordered_set out; + for (std::vector::const_iterator it = objs.begin(); it != objs.end(); ++it) { + std::map::iterator jt = ObjectMap.find(*it); + // ok, object is part of this graph + if (jt != ObjectMap.end()) { + for (boost::tie(j, jend) = boost::out_edges(jt->second, DepList); j != jend; ++j) { + out.insert(VertexMap[boost::target(*j, DepList)]); + } + out.insert(*it); + } + } + + std::vector ary; + ary.insert(ary.end(), out.begin(), out.end()); + return ary; +} + void Document::_rebuildDependencyList(void) { d->VertexObjectList.clear(); @@ -1119,21 +1217,6 @@ void Document::recompute() // updates the dependency graph _rebuildDependencyList(); - //DependencyList DepList; - //std::map VertexObjectList; - - //// Filling up the adjacency List - //for (std::map::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) - // // add the object as Vertex and remember the index - // VertexObjectList[It->second] = add_vertex(DepList); - //// add the edges - //for (std::map::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) { - // std::vector OutList = It->second->getOutList(); - // for (std::vector::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) - // if (*It2) - // add_edge(VertexObjectList[It->second],VertexObjectList[*It2],DepList); - //} - std::list make_order; DependencyList::out_edge_iterator j, jend; diff --git a/src/App/Document.h b/src/App/Document.h index 5e2eedd89..3ab0cb639 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -133,6 +133,7 @@ public: //void saveAs (const char* Name); /// Save the document to the file in Property Path bool save (void); + bool saveAs(const char* file); /// Restore the document from the file in Property Path void restore (void); void exportObjects(const std::vector&, std::ostream&); @@ -264,6 +265,10 @@ public: bool checkOnCycle(void); /// get a list of all objects linking to the given object std::vector getInList(const DocumentObject* me) const; + /// Get a complete list of all objects the given objects depend on. The list + /// also contains the given objects! + std::vector getDependencyList + (const std::vector&) const; // set Changed //void setChanged(DocumentObject* change); //@} @@ -303,6 +308,7 @@ protected: void _clearRedos(); /// refresh the internal dependency graph void _rebuildDependencyList(void); + std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const; private: diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 78616408c..079c361c8 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -38,6 +38,15 @@ namespace App class Document; class DocumentObjectPy; +enum ObjectStatus { + Touch = 0, + Error = 1, + New = 2, + Recompute = 3, + Restore = 4, + Expand = 16 +}; + /** Return object for feature execution */ class AppExport DocumentObjectExecReturn @@ -105,9 +114,11 @@ public: virtual App::DocumentObjectExecReturn *recompute(void); /// return the status bits unsigned long getStatus() const {return StatusBits.to_ulong();} + bool testStatus(ObjectStatus pos) const {return StatusBits.test((size_t)pos);} + void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);} //@} - /// returns a list of objects this object is pointing to by Links + /// returns a list of objects this object is pointing to by Links std::vector getOutList(void) const; /// get all objects link to this object std::vector getInList(void) const; @@ -129,7 +140,7 @@ public: /** Called in case of loosing a link * Get called by the document when a object got deleted a link property of this - * object ist pointing to. The standard behaivour of the DocumentObject implementation + * object ist pointing to. The standard behaviour of the DocumentObject implementation * is to reset the links to nothing. You may overide this method to implement *additional or different behavior. */ @@ -160,14 +171,15 @@ protected: * The first 8 bits are used for the base system the rest can be used in * descendent classes to to mark special stati on the objects. * The bits and their meaning are listed below: - * 0 - object is marked as 'touched' - * 1 - object is marked as 'erroneous' - * 2 - object is marked as 'new' - * 3 - object is marked as 'recompute', i.e. the object gets recomputed now - * 4 - object is marked as 'restoring', i.e. the object gets loaded at the moment - * 5 - reserved - * 6 - reserved - * 7 - reserved + * 0 - object is marked as 'touched' + * 1 - object is marked as 'erroneous' + * 2 - object is marked as 'new' + * 3 - object is marked as 'recompute', i.e. the object gets recomputed now + * 4 - object is marked as 'restoring', i.e. the object gets loaded at the moment + * 5 - reserved + * 6 - reserved + * 7 - reserved + * 16 - object is marked as 'expanded' in the tree view */ std::bitset<32> StatusBits; diff --git a/src/App/DocumentObserverPython.cpp b/src/App/DocumentObserverPython.cpp index eeac4886c..e844ca7b7 100644 --- a/src/App/DocumentObserverPython.cpp +++ b/src/App/DocumentObserverPython.cpp @@ -101,7 +101,7 @@ void DocumentObserverPython::slotCreatedDocument(const App::Document& Doc) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -118,7 +118,7 @@ void DocumentObserverPython::slotDeletedDocument(const App::Document& Doc) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -135,7 +135,7 @@ void DocumentObserverPython::slotRelabelDocument(const App::Document& Doc) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -152,7 +152,7 @@ void DocumentObserverPython::slotActivateDocument(const App::Document& Doc) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -169,7 +169,7 @@ void DocumentObserverPython::slotCreatedObject(const App::DocumentObject& Obj) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -186,7 +186,7 @@ void DocumentObserverPython::slotDeletedObject(const App::DocumentObject& Obj) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -206,6 +206,6 @@ void DocumentObserverPython::slotChangedObject(const App::DocumentObject& Obj, } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } diff --git a/src/App/DocumentPy.xml b/src/App/DocumentPy.xml index 4a996e111..7aa4b2304 100644 --- a/src/App/DocumentPy.xml +++ b/src/App/DocumentPy.xml @@ -15,12 +15,17 @@ - Save the document to disc + Save the document to disk + + + + + Save the document under a new name to disk - Restore the document from disc + Restore the document from disk diff --git a/src/App/DocumentPyImp.cpp b/src/App/DocumentPyImp.cpp index cc9c40e62..c5225ca11 100644 --- a/src/App/DocumentPyImp.cpp +++ b/src/App/DocumentPyImp.cpp @@ -69,6 +69,25 @@ PyObject* DocumentPy::save(PyObject * args) Py_Return; } +PyObject* DocumentPy::saveAs(PyObject * args) +{ + char* fn; + if (!PyArg_ParseTuple(args, "s", &fn)) // convert args: Python->C + return NULL; // NULL triggers exception + if (!getDocumentPtr()->saveAs(fn)) { + PyErr_Format(PyExc_ValueError, "Object attribute 'FileName' is not set"); + return NULL; + } + + Base::FileInfo fi(fn); + if (!fi.isReadable()) { + PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", fn); + return NULL; + } + + Py_Return; +} + PyObject* DocumentPy::restore(PyObject * args) { if (!PyArg_ParseTuple(args, "")) // convert args: Python->C diff --git a/src/App/FeaturePython.cpp b/src/App/FeaturePython.cpp index a4b1a0e12..9348ace12 100644 --- a/src/App/FeaturePython.cpp +++ b/src/App/FeaturePython.cpp @@ -67,6 +67,7 @@ DocumentObjectExecReturn *FeaturePythonImp::execute() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text + e.ReportException(); std::stringstream str; str << object->Label.getValue() << ": " << e.what(); return new App::DocumentObjectExecReturn(str.str()); @@ -104,8 +105,7 @@ void FeaturePythonImp::onChanged(const Property* prop) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("FeaturePython::onChanged (%s): %s\n", - object->Label.getValue(), e.what()); + e.ReportException(); } } diff --git a/src/App/Property.h b/src/App/Property.h index b1046aa34..13f79d8fd 100644 --- a/src/App/Property.h +++ b/src/App/Property.h @@ -61,7 +61,7 @@ public: */ virtual unsigned int getMemSize (void) const { // you have to implement this method in all property classes! - return Base::Persistence::getMemSize() + sizeof(father) + sizeof(StatusBits); + return sizeof(father) + sizeof(StatusBits); } /// get the name of this property in the belonging container diff --git a/src/App/PropertyFile.cpp b/src/App/PropertyFile.cpp index 934023272..5858d8e1e 100644 --- a/src/App/PropertyFile.cpp +++ b/src/App/PropertyFile.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include #endif @@ -35,12 +36,13 @@ #include #include #include +#include #include "PropertyFile.h" #include "Document.h" #include "PropertyContainer.h" #include "DocumentObject.h" -#define new DEBUG_CLIENTBLOCK + using namespace App; using namespace Base; using namespace std; @@ -53,6 +55,7 @@ using namespace std; TYPESYSTEM_SOURCE(App::PropertyFileIncluded , App::Property); + PropertyFileIncluded::PropertyFileIncluded() { @@ -63,17 +66,45 @@ PropertyFileIncluded::~PropertyFileIncluded() // clean up if (!_cValue.empty()) { Base::FileInfo file(_cValue.c_str()); + file.setPermissions(Base::FileInfo::ReadWrite); file.deleteFile(); } } +void PropertyFileIncluded::aboutToSetValue(void) +{ + // This is a trick to check in Copy() if it is called + // directly from outside or by the Undo/Redo mechanism. + // In the latter case it is sufficient to rename the file + // because another file will be assigned afterwards. + // If Copy() is directly called (e.g. to copy the file to + // another document) a copy of the file needs to be created. + // This copy will be deleted again in the class destructor. + this->StatusBits.set(10); + Property::aboutToSetValue(); + this->StatusBits.reset(10); +} + std::string PropertyFileIncluded::getDocTransientPath(void) const { + std::string path; PropertyContainer *co = getContainer(); - if (co->isDerivedFrom(DocumentObject::getClassTypeId())) - return dynamic_cast(co)->getDocument()->TransientDir.getValue(); + if (co->isDerivedFrom(DocumentObject::getClassTypeId())) { + path = dynamic_cast(co)->getDocument()->TransientDir.getValue(); + std::replace(path.begin(), path.end(), '\\', '/'); + } + return path; +} - return std::string(); +std::string PropertyFileIncluded::getUniqueFileName(const std::string& path, const std::string& filename) const +{ + Base::Uuid uuid; + Base::FileInfo fi(path + "/" + filename); + while (fi.exists()) { + fi.setFile(path + "/" + filename + "." + uuid.getValue()); + } + + return fi.filePath(); } std::string PropertyFileIncluded::getExchangeTempFile(void) const @@ -97,34 +128,40 @@ void PropertyFileIncluded::setValue(const char* sFile, const char* sName) throw Base::Exception(str.str()); } - aboutToSetValue(); // undo redo by move the file away with temp name + aboutToSetValue(); // undo/redo by moving the file away with temp name // remove old file (if not moved by undo) Base::FileInfo value(_cValue); std::string pathAct = value.dirPath(); - if (value.exists()) + if (value.exists()) { + value.setPermissions(Base::FileInfo::ReadWrite); value.deleteFile(); + } // if a special name given, use this instead if (sName) { - Base::FileInfo ExtraName(path + "/" + sName); - if (ExtraName.exists() ) { + Base::FileInfo fi(pathTrans + "/" + sName); + if (fi.exists()) { // if a file with this name already exists search for a new one + std::string dir = pathTrans; + std::string fnp = fi.fileNamePure(); + std::string ext = fi.extension(false); int i=0; - do { i++; std::stringstream str; - str << path << "/" << sName << i; - ExtraName.setFile(str.str()); + str << dir << "/" << fnp << i; + if (!ext.empty()) + str << "." << ext; + fi.setFile(str.str()); } - while (ExtraName.exists()); - _cValue = ExtraName.filePath(); - _BaseFileName = ExtraName.fileName(); + while (fi.exists()); + _cValue = fi.filePath(); + _BaseFileName = fi.fileName(); } else { - _cValue = path + "/" + sName; + _cValue = pathTrans + "/" + sName; _BaseFileName = sName; } } @@ -133,39 +170,61 @@ void PropertyFileIncluded::setValue(const char* sFile, const char* sName) _BaseFileName = file.fileName(); } - // if the files is already in transient dir of the document, just use it - if (path == pathTrans) { + // The following applies only on files that are inside the transient + // directory: + // When a file is read-only it is supposed to be assigned to a + // PropertyFileIncluded instance. In this case we must copy the + // file because otherwise the above instance looses its data. + // If the file is writable it is supposed to be of free use and + // it can be simply renamed. + + // if the file is already in transient dir of the document, just use it + if (path == pathTrans && file.isWritable()) { bool done = file.renameFile(_cValue.c_str()); - //assert(done); if (!done) { std::stringstream str; str << "Cannot rename file " << file.filePath() << " to " << _cValue; throw Base::Exception(str.str()); } + + // make the file read-only + Base::FileInfo dst(_cValue); + dst.setPermissions(Base::FileInfo::ReadOnly); } // otherwise copy from origin location else { // if file already exists in transient dir make a new unique name Base::FileInfo fi(_cValue); if (fi.exists()) { - Base::FileInfo fi2(Base::FileInfo::getTempFileName()); - std::stringstream str; - str << fi.dirPath() << "/" << fi2.fileNamePure(); + // if a file with this name already exists search for a new one + std::string dir = fi.dirPath(); + std::string fnp = fi.fileNamePure(); std::string ext = fi.extension(false); - if (!ext.empty()) - str << "." << ext; - Base::FileInfo fi3(str.str()); - _cValue = fi3.filePath(); - _BaseFileName = fi3.fileName(); + int i=0; + do { + i++; + std::stringstream str; + str << dir << "/" << fnp << i; + if (!ext.empty()) + str << "." << ext; + fi.setFile(str.str()); + } + while (fi.exists()); + + _cValue = fi.filePath(); + _BaseFileName = fi.fileName(); } bool done = file.copyTo(_cValue.c_str()); - //assert(done); if (!done) { std::stringstream str; str << "Cannot copy file from " << file.filePath() << " to " << _cValue; throw Base::Exception(str.str()); } + + // make the file read-only + Base::FileInfo dst(_cValue); + dst.setPermissions(Base::FileInfo::ReadOnly); } hasSetValue(); @@ -180,7 +239,7 @@ const char* PropertyFileIncluded::getValue(void) const PyObject *PropertyFileIncluded::getPyObject(void) { PyObject *p = PyUnicode_DecodeUTF8(_cValue.c_str(),_cValue.size(),0); - if (!p) throw Base::Exception("UTF8 conversion failure at PropertyString::getPyObject()"); + if (!p) throw Base::Exception("PropertyFileIncluded: UTF-8 conversion failure"); return p; } @@ -201,7 +260,7 @@ void PropertyFileIncluded::setPyObject(PyObject *value) } else if (PyTuple_Check(value)) { if (PyTuple_Size(value) != 2) - throw Py::TypeError("Tuple need size of (filePath,newFileName)"); + throw Base::TypeError("Tuple needs size of (filePath,newFileName)"); PyObject* file = PyTuple_GetItem(value,0); PyObject* name = PyTuple_GetItem(value,1); @@ -220,9 +279,9 @@ void PropertyFileIncluded::setPyObject(PyObject *value) fileStr = PyString_AsString(FileName); } else { - std::string error = std::string("first in tuple must be a file or string"); + std::string error = std::string("First item in tuple must be a file or string"); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } // decoding name @@ -235,19 +294,18 @@ void PropertyFileIncluded::setPyObject(PyObject *value) nameStr = PyString_AsString(FileName); } else { - std::string error = std::string("second in tuple must be a string"); + std::string error = std::string("Second item in tuple must be a string"); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } setValue(fileStr.c_str(),nameStr.c_str()); return; - } else { - std::string error = std::string("type must be str or file"); + std::string error = std::string("Type must be string or file"); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } // assign the string @@ -256,29 +314,39 @@ void PropertyFileIncluded::setPyObject(PyObject *value) void PropertyFileIncluded::Save (Base::Writer &writer) const { + // when saving a document under a new file name the transient directory + // name changes and thus the stored file name doesn't work any more. + if (!_cValue.empty() && !Base::FileInfo(_cValue).exists()) { + Base::FileInfo fi(getDocTransientPath() + "/" + _BaseFileName); + if (fi.exists()) + _cValue = fi.filePath(); + } + if (writer.isForceXML()) { if (!_cValue.empty()) { Base::FileInfo file(_cValue.c_str()); - writer.Stream() << writer.ind() << "" << std::endl; + writer.Stream() << writer.ind() << "" << std::endl; // write the file in the XML stream writer.incInd(); writer.insertBinFile(_cValue.c_str()); writer.decInd(); writer.Stream() << writer.ind() <<"" << endl; } - else + else { writer.Stream() << writer.ind() << "" << std::endl; + } } else { // instead initiate an extra file if (!_cValue.empty()) { Base::FileInfo file(_cValue.c_str()); - writer.Stream() << writer.ind() << "" << std::endl; + writer.Stream() << writer.ind() << "" << std::endl; } - else + else { writer.Stream() << writer.ind() << "" << std::endl; + } } } @@ -307,6 +375,9 @@ void PropertyFileIncluded::Restore(Base::XMLReader &reader) reader.readBinFile(_cValue.c_str()); reader.readEndElement("FileIncluded"); _BaseFileName = file; + // set read-only after restoring the file + Base::FileInfo fi(_cValue.c_str()); + fi.setPermissions(Base::FileInfo::ReadOnly); hasSetValue(); } } @@ -315,9 +386,12 @@ void PropertyFileIncluded::Restore(Base::XMLReader &reader) void PropertyFileIncluded::SaveDocFile (Base::Writer &writer) const { Base::ifstream from(Base::FileInfo(_cValue.c_str())); - if (!from) - throw Base::Exception("PropertyFileIncluded::SaveDocFile() " - "File in document transient dir deleted"); + if (!from) { + std::stringstream str; + str << "PropertyFileIncluded::SaveDocFile(): " + << "File '" << _cValue << "' in transient directory doesn't exist."; + throw Base::Exception(str.str()); + } // copy plain data unsigned char c; @@ -329,10 +403,14 @@ void PropertyFileIncluded::SaveDocFile (Base::Writer &writer) const void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader) { - Base::ofstream to(Base::FileInfo(_cValue.c_str())); - if (!to) - throw Base::Exception("PropertyFileIncluded::RestoreDocFile() " - "File in document transient dir deleted"); + Base::FileInfo fi(_cValue.c_str()); + Base::ofstream to(fi); + if (!to) { + std::stringstream str; + str << "PropertyFileIncluded::RestoreDocFile(): " + << "File '" << _cValue << "' in transient directory doesn't exist."; + throw Base::Exception(str.str()); + } // copy plain data aboutToSetValue(); @@ -341,6 +419,9 @@ void PropertyFileIncluded::RestoreDocFile(Base::Reader &reader) to.put((const char)c); } to.close(); + + // set read-only after restoring the file + fi.setPermissions(Base::FileInfo::ReadOnly); hasSetValue(); } @@ -351,18 +432,39 @@ Property *PropertyFileIncluded::Copy(void) const // remember the base name prop->_BaseFileName = _BaseFileName; - if (!_cValue.empty()) { - Base::FileInfo file(_cValue); - + Base::FileInfo file(_cValue); + if (file.exists()) { // create a new name in the document transient directory - Base::FileInfo NewName(Base::FileInfo::getTempFileName(file.fileName().c_str(),file.dirPath().c_str())); - NewName.deleteFile(); - // move the file - bool done = file.renameFile(NewName.filePath().c_str()); - assert(done); + Base::FileInfo newName(getUniqueFileName(file.dirPath(), file.fileName())); + if (this->StatusBits.test(10)) { + // rename the file + bool done = file.renameFile(newName.filePath().c_str()); + if (!done) { + std::stringstream str; + str << "PropertyFileIncluded::Copy(): " + << "Renaming the file '" << file.filePath() << "' to '" + << newName.filePath() << "' failed."; + throw Base::Exception(str.str()); + } + } + else { + // copy the file + bool done = file.copyTo(newName.filePath().c_str()); + if (!done) { + std::stringstream str; + str << "PropertyFileIncluded::Copy(): " + << "Copying the file '" << file.filePath() << "' to '" + << newName.filePath() << "' failed."; + throw Base::Exception(str.str()); + } + } + // remember the new name for the Undo - Base::Console().Log("Copy this=%p Before=%s After=%s\n",prop,prop->_cValue.c_str(),NewName.filePath().c_str()); - prop->_cValue = NewName.filePath().c_str(); + Base::Console().Log("Copy '%s' to '%s'\n",_cValue.c_str(),newName.filePath().c_str()); + prop->_cValue = newName.filePath().c_str(); + + // make backup files writable to avoid copying them again on undo/redo + newName.setPermissions(Base::FileInfo::ReadWrite); } return prop; @@ -371,26 +473,65 @@ Property *PropertyFileIncluded::Copy(void) const void PropertyFileIncluded::Paste(const Property &from) { aboutToSetValue(); - Base::FileInfo file(_cValue); - // delete old file (if still there) - file.deleteFile(); - const PropertyFileIncluded &fileInc = dynamic_cast(from); + const PropertyFileIncluded &prop = dynamic_cast(from); + // make sure that source and destination file are different + if (_cValue != prop._cValue) { + // delete old file (if still there) + Base::FileInfo fi(_cValue); + fi.setPermissions(Base::FileInfo::ReadWrite); + fi.deleteFile(); - // set the base name - _BaseFileName = fileInc._BaseFileName; + // get path to destination which can be the transient directory + // of another document + std::string pathTrans = getDocTransientPath(); + Base::FileInfo fiSrc(prop._cValue); + Base::FileInfo fiDst(pathTrans + "/" + prop._BaseFileName); + std::string path = fiSrc.dirPath(); - if (!fileInc._cValue.empty()) { - // move the saved files back in place - Base::FileInfo NewFile(fileInc._cValue); - _cValue = NewFile.dirPath() + "/" + fileInc._BaseFileName; - bool done = NewFile.renameFile(_cValue.c_str()); - assert(done); + if (fiSrc.exists()) { + fiDst.setFile(getUniqueFileName(fiDst.dirPath(), fiDst.fileName())); + + // if the file is already in transient dir of the document, just use it + if (path == pathTrans) { + if (!fiSrc.renameFile(fiDst.filePath().c_str())) { + std::stringstream str; + str << "PropertyFileIncluded::Paste(): " + << "Renaming the file '" << fiSrc.filePath() << "' to '" + << fiDst.filePath() << "' failed."; + throw Base::Exception(str.str()); + } + } + else { + if (!fiSrc.copyTo(fiDst.filePath().c_str())) { + std::stringstream str; + str << "PropertyFileIncluded::Paste(): " + << "Copying the file '" << fiSrc.filePath() << "' to '" + << fiDst.filePath() << "' failed."; + throw Base::Exception(str.str()); + } + } + + // set the file again read-only + fiDst.setPermissions(Base::FileInfo::ReadOnly); + _cValue = fiDst.filePath(); + } + else { + _cValue.clear(); + } + + // set the base name + _BaseFileName = prop._BaseFileName; } - else - _cValue.clear(); hasSetValue(); } +unsigned int PropertyFileIncluded::getMemSize (void) const +{ + unsigned int mem = Property::getMemSize(); + mem += _cValue.size(); + mem += _BaseFileName.size(); + return mem; +} //************************************************************************** // PropertyFile diff --git a/src/App/PropertyFile.h b/src/App/PropertyFile.h index 270fe35c9..d69f93148 100644 --- a/src/App/PropertyFile.h +++ b/src/App/PropertyFile.h @@ -95,9 +95,7 @@ public: virtual Property *Copy(void) const; virtual void Paste(const Property &from); - - // get the transient path if the property is in a DocumentObject - std::string getDocTransientPath(void) const; + virtual unsigned int getMemSize (void) const; /** get a temp file name in the transient path of the document. * Using this file for new Version of the file and set @@ -107,8 +105,14 @@ public: std::string getExchangeTempFile(void) const; protected: - std::string _cValue; - std::string _BaseFileName; + // get the transient path if the property is in a DocumentObject + std::string getDocTransientPath(void) const; + std::string getUniqueFileName(const std::string&, const std::string&) const; + void aboutToSetValue(void); + +protected: + mutable std::string _cValue; + mutable std::string _BaseFileName; }; diff --git a/src/App/PropertyGeo.cpp b/src/App/PropertyGeo.cpp index 5a1adb114..47b718d67 100644 --- a/src/App/PropertyGeo.cpp +++ b/src/App/PropertyGeo.cpp @@ -117,7 +117,7 @@ void PropertyVector::setPyObject(PyObject *value) else if (PyInt_Check(item)) cVec.x = (float)PyInt_AsLong(item); else - throw Base::Exception("Not allowed type used in tuple (float expected)..."); + throw Base::TypeError("Not allowed type used in tuple (float expected)..."); // y item = PyTuple_GetItem(value,1); if (PyFloat_Check(item)) @@ -125,7 +125,7 @@ void PropertyVector::setPyObject(PyObject *value) else if (PyInt_Check(item)) cVec.y = (float)PyInt_AsLong(item); else - throw Base::Exception("Not allowed type used in tuple (float expected)..."); + throw Base::TypeError("Not allowed type used in tuple (float expected)..."); // z item = PyTuple_GetItem(value,2); if (PyFloat_Check(item)) @@ -133,13 +133,13 @@ void PropertyVector::setPyObject(PyObject *value) else if (PyInt_Check(item)) cVec.z = (float)PyInt_AsLong(item); else - throw Base::Exception("Not allowed type used in tuple (float expected)..."); + throw Base::TypeError("Not allowed type used in tuple (float expected)..."); setValue( cVec ); } else { std::string error = std::string("type must be 'Vector' or tuple of three floats, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -271,7 +271,7 @@ void PropertyVectorList::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Vector' or list of 'Vector', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -396,7 +396,7 @@ void PropertyMatrix::setPyObject(PyObject *value) else if (PyInt_Check(item)) cMatrix[x][y] = (double)PyInt_AsLong(item); else - throw Base::Exception("Not allowed type used in matrix tuple (a number expected)..."); + throw Base::TypeError("Not allowed type used in matrix tuple (a number expected)..."); } } @@ -405,7 +405,7 @@ void PropertyMatrix::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Matrix' or tuple of 16 float or int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -520,7 +520,7 @@ void PropertyPlacement::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Matrix' or 'Placement', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index bba19fcd1..7cc729aee 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -110,7 +110,7 @@ void PropertyLink::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'DocumentObject' or 'NoneType', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -257,7 +257,7 @@ void PropertyLinkSub::setPyObject(PyObject *value) else { std::string error = std::string("type of first element in tuple must be 'DocumentObject', not "); error += tup[0].ptr()->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } else if(Py_None == value) { @@ -266,7 +266,7 @@ void PropertyLinkSub::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'DocumentObject', 'NoneType' or ('DocumentObject',['String',]) not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -409,7 +409,7 @@ void PropertyLinkList::setPyObject(PyObject *value) if (!PyObject_TypeCheck(*item, &(DocumentObjectPy::Type))) { std::string error = std::string("type in list must be 'DocumentObject', not "); error += (*item)->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = static_cast(*item)->getDocumentObjectPtr(); @@ -424,7 +424,7 @@ void PropertyLinkList::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'DocumentObject' or list of 'DocumentObject', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -609,7 +609,7 @@ void PropertyLinkSubList::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'DocumentObject' or list of 'DocumentObject', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/App/PropertyPythonObject.cpp b/src/App/PropertyPythonObject.cpp index 0cdff1ae8..df42a5a73 100644 --- a/src/App/PropertyPythonObject.cpp +++ b/src/App/PropertyPythonObject.cpp @@ -108,7 +108,7 @@ std::string PropertyPythonObject::toString() const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Warning("PropertyPythonObject::toString: %s\n", e.what()); + e.ReportException(); } return repr; @@ -139,7 +139,7 @@ void PropertyPythonObject::fromString(const std::string& repr) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Warning("PropertyPythonObject::fromString: %s\n", e.what()); + e.ReportException(); } } @@ -165,7 +165,7 @@ void PropertyPythonObject::loadPickle(const std::string& str) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Warning("PropertyPythonObject::loadPickle: %s\n", e.what()); + e.ReportException(); } } @@ -283,7 +283,7 @@ void PropertyPythonObject::Save (Base::Writer &writer) const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Warning("PropertyPythonObject::Save: %s\n", e.what()); + e.ReportException(); } saveObject(writer); @@ -350,7 +350,7 @@ void PropertyPythonObject::Restore(Base::XMLReader &reader) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Warning("PropertyPythonObject::Restore: %s\n", e.what()); + e.ReportException(); this->object = Py::None(); load_failed = true; } diff --git a/src/App/PropertyStandard.cpp b/src/App/PropertyStandard.cpp index fb73ec4d0..573538d6c 100644 --- a/src/App/PropertyStandard.cpp +++ b/src/App/PropertyStandard.cpp @@ -98,7 +98,7 @@ void PropertyInteger::setPyObject(PyObject *value) else { std::string error = std::string("type must be int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -211,7 +211,7 @@ void PropertyPath::setPyObject(PyObject *value) else { std::string error = std::string("type must be str or unicode, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } // assign the path @@ -472,7 +472,7 @@ void PropertyEnumeration::setPyObject(PyObject *value) long i=0; while(*(plEnums++) != NULL)i++; if (val < 0 || i <= val) - throw Py::ValueError("Out of range"); + throw Base::ValueError("Out of range"); PropertyInteger::setValue(val); } } @@ -481,7 +481,7 @@ void PropertyEnumeration::setPyObject(PyObject *value) if (_EnumArray && isPartOf(str)) setValue(PyString_AsString (value)); else - throw Py::ValueError("not part of the enum"); + throw Base::ValueError("not part of the enum"); } else if (PyList_Check(value)) { Py_ssize_t nSize = PyList_Size(value); @@ -493,7 +493,7 @@ void PropertyEnumeration::setPyObject(PyObject *value) if (!PyString_Check(item)) { std::string error = std::string("type in list must be str, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = PyString_AsString(item); } @@ -505,10 +505,33 @@ void PropertyEnumeration::setPyObject(PyObject *value) else { std::string error = std::string("type must be int or str, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } +Property *PropertyEnumeration::Copy(void) const +{ + PropertyEnumeration *p= new PropertyEnumeration(); + p->_lValue = _lValue; + if (_CustomEnum) { + p->_CustomEnum = true; + p->setEnumVector(getEnumVector()); + } + return p; +} + +void PropertyEnumeration::Paste(const Property &from) +{ + aboutToSetValue(); + const PropertyEnumeration& prop = dynamic_cast(from); + _lValue = prop._lValue; + if (prop._CustomEnum) { + this->_CustomEnum = true; + this->setEnumVector(prop.getEnumVector()); + } + hasSetValue(); +} + //************************************************************************** //************************************************************************** // PropertyIntegerConstraint @@ -559,7 +582,7 @@ void PropertyIntegerConstraint::setPyObject(PyObject *value) else { std::string error = std::string("type must be int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -654,7 +677,7 @@ void PropertyIntegerList::setPyObject(PyObject *value) if (!PyInt_Check(item)) { std::string error = std::string("type in list must be int, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = PyInt_AsLong(item); } @@ -667,7 +690,7 @@ void PropertyIntegerList::setPyObject(PyObject *value) else { std::string error = std::string("type must be int or list of int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -782,7 +805,7 @@ void PropertyIntegerSet::setPyObject(PyObject *value) if (!PyInt_Check(item)) { std::string error = std::string("type in list must be int, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values.insert(PyInt_AsLong(item)); } @@ -795,7 +818,7 @@ void PropertyIntegerSet::setPyObject(PyObject *value) else { std::string error = std::string("type must be int or list of int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -822,7 +845,7 @@ void PropertyIntegerSet::Restore(Base::XMLReader &reader) values.insert(reader.getAttributeAsInteger("v")); } - reader.readEndElement("IntegerList"); + reader.readEndElement("IntegerSet"); //assignment setValues(values); @@ -905,7 +928,7 @@ void PropertyFloat::setPyObject(PyObject *value) else { std::string error = std::string("type must be float or int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -999,7 +1022,7 @@ void PropertyFloatConstraint::setPyObject(PyObject *value) else { std::string error = std::string("type must be float, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1072,7 +1095,7 @@ void PropertyFloatList::setPyObject(PyObject *value) if (!PyFloat_Check(item)) { std::string error = std::string("type in list must be float, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = (float) PyFloat_AsDouble(item); @@ -1086,7 +1109,7 @@ void PropertyFloatList::setPyObject(PyObject *value) else { std::string error = std::string("type must be float or list of float, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1217,7 +1240,7 @@ void PropertyString::setPyObject(PyObject *value) else { std::string error = std::string("type must be str or unicode, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } // assign the string @@ -1322,11 +1345,18 @@ void PropertyUUID::setPyObject(PyObject *value) else { std::string error = std::string("type must be a str, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } - // assign the string - setValue(string); + try { + // assign the string + Base::Uuid uid; + uid.setValue(string); + setValue(uid); + } + catch (const std::exception& e) { + throw Base::RuntimeError(e.what()); + } } void PropertyUUID::Save (Base::Writer &writer) const @@ -1465,7 +1495,7 @@ void PropertyStringList::setPyObject(PyObject *value) else { std::string error = std::string("type in list must be str or unicode, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1477,7 +1507,7 @@ void PropertyStringList::setPyObject(PyObject *value) else { std::string error = std::string("type must be str or list of str, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1625,7 +1655,7 @@ void PropertyMap::setPyObject(PyObject *value) else { std::string error = std::string("type of the key need to be a string, not"); error += key->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } // check on the item: @@ -1641,7 +1671,7 @@ void PropertyMap::setPyObject(PyObject *value) else { std::string error = std::string("type in list must be string or unicode, not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1650,7 +1680,7 @@ void PropertyMap::setPyObject(PyObject *value) else { std::string error = std::string("type must be a dict object"); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1764,7 +1794,7 @@ void PropertyBool::setPyObject(PyObject *value) else { std::string error = std::string("type must be bool, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -1876,17 +1906,17 @@ void PropertyColor::setPyObject(PyObject *value) if (PyFloat_Check(item)) cCol.r = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); item = PyTuple_GetItem(value,1); if (PyFloat_Check(item)) cCol.g = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); item = PyTuple_GetItem(value,2); if (PyFloat_Check(item)) cCol.b = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); } else if (PyTuple_Check(value) && PyTuple_Size(value) == 4) { PyObject* item; @@ -1894,22 +1924,22 @@ void PropertyColor::setPyObject(PyObject *value) if (PyFloat_Check(item)) cCol.r = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); item = PyTuple_GetItem(value,1); if (PyFloat_Check(item)) cCol.g = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); item = PyTuple_GetItem(value,2); if (PyFloat_Check(item)) cCol.b = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); item = PyTuple_GetItem(value,3); if (PyFloat_Check(item)) cCol.a = (float)PyFloat_AsDouble(item); else - throw Base::Exception("Type in tuple must be float"); + throw Base::TypeError("Type in tuple must be float"); } else if (PyLong_Check(value)) { cCol.setPackedValue(PyLong_AsUnsignedLong(value)); @@ -1917,7 +1947,7 @@ void PropertyColor::setPyObject(PyObject *value) else { std::string error = std::string("type must be int or tuple of float, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } setValue( cCol ); @@ -2050,7 +2080,7 @@ void PropertyColorList::setPyObject(PyObject *value) else { std::string error = std::string("not allowed type, "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } @@ -2201,7 +2231,7 @@ void PropertyMaterial::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Material', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/App/PropertyStandard.h b/src/App/PropertyStandard.h index e611dd070..b23d9e4d7 100644 --- a/src/App/PropertyStandard.h +++ b/src/App/PropertyStandard.h @@ -183,6 +183,9 @@ public: virtual void Save (Base::Writer &writer) const; virtual void Restore(Base::XMLReader &reader); + virtual Property *Copy(void) const; + virtual void Paste(const Property &from); + private: bool _CustomEnum; const char** _EnumArray; diff --git a/src/App/PropertyUnits.cpp b/src/App/PropertyUnits.cpp index fae1df405..e20f3e698 100644 --- a/src/App/PropertyUnits.cpp +++ b/src/App/PropertyUnits.cpp @@ -102,11 +102,11 @@ void PropertyLength::setPyObject(PyObject *value) else { std::string error = std::string("type must be float or int, not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } if (val < 0.0f) - throw Py::ValueError("value must be nonnegative"); + throw Base::ValueError("value must be nonnegative"); setValue(val); #endif diff --git a/src/Base/BaseClassPy.xml b/src/Base/BaseClassPy.xml index 7795e901a..4d2122047 100644 --- a/src/Base/BaseClassPy.xml +++ b/src/Base/BaseClassPy.xml @@ -24,11 +24,11 @@ Returns all descentences - + Is the type of the FreeCAD object with module domain - + diff --git a/src/Base/BaseClassPyImp.cpp b/src/Base/BaseClassPyImp.cpp index 8ba276066..1ff47646c 100644 --- a/src/Base/BaseClassPyImp.cpp +++ b/src/Base/BaseClassPyImp.cpp @@ -68,7 +68,7 @@ PyObject* BaseClassPy::getAllDerivedFrom(PyObject *args) return Py::new_reference_to(res); } -Py::String BaseClassPy::getType(void) const +Py::String BaseClassPy::getTypeId(void) const { return Py::String(std::string(getBaseClassPtr()->getTypeId().getName())); } @@ -78,8 +78,15 @@ Py::Int BaseClassPy::getModule(void) const return Py::Int(); } -PyObject *BaseClassPy::getCustomAttributes(const char* /*attr*/) const +PyObject *BaseClassPy::getCustomAttributes(const char* attr) const { + // this attribute is marked 'deprecated' but to keep old code working we + // handle it here. In a future version this will be removed. + if (strcmp(attr, "Type") == 0) { + PyErr_SetString(PyExc_DeprecationWarning, "Use 'TypeId' instead"); + PyErr_Print(); + return Py::new_reference_to(Py::String(std::string(getBaseClassPtr()->getTypeId().getName()))); + } return 0; } diff --git a/src/Base/Exception.cpp b/src/Base/Exception.cpp index 4ca62890c..2d7b58c25 100644 --- a/src/Base/Exception.cpp +++ b/src/Base/Exception.cpp @@ -236,6 +236,74 @@ ProgramInformation::ProgramInformation(const ProgramInformation &inst) // --------------------------------------------------------- +TypeError::TypeError(const char * sMessage) + : Exception(sMessage) +{ +} + +TypeError::TypeError(const std::string& sMessage) + : Exception(sMessage) +{ +} + +TypeError::TypeError(const TypeError &inst) + : Exception(inst) +{ +} + +// --------------------------------------------------------- + +ValueError::ValueError(const char * sMessage) + : Exception(sMessage) +{ +} + +ValueError::ValueError(const std::string& sMessage) + : Exception(sMessage) +{ +} + +ValueError::ValueError(const ValueError &inst) + : Exception(inst) +{ +} + +// --------------------------------------------------------- + +AttributeError::AttributeError(const char * sMessage) + : Exception(sMessage) +{ +} + +AttributeError::AttributeError(const std::string& sMessage) + : Exception(sMessage) +{ +} + +AttributeError::AttributeError(const AttributeError &inst) + : Exception(inst) +{ +} + +// --------------------------------------------------------- + +RuntimeError::RuntimeError(const char * sMessage) + : Exception(sMessage) +{ +} + +RuntimeError::RuntimeError(const std::string& sMessage) + : Exception(sMessage) +{ +} + +RuntimeError::RuntimeError(const RuntimeError &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 19bd9155c..391cc443d 100644 --- a/src/Base/Exception.h +++ b/src/Base/Exception.h @@ -48,7 +48,7 @@ public: Exception &operator=(const Exception &inst); virtual const char* what(void) const throw(); - void ReportException (void) const; + virtual void ReportException (void) const; inline void setMessage(const char * sMessage); inline void setMessage(const std::string& sMessage); @@ -206,6 +206,70 @@ public: virtual ~ProgramInformation() throw() {} }; +/** + * The TypeError can be used to indicate the usage of a wrong type. + * @author Werner Mayer + */ +class BaseExport TypeError : public Exception +{ +public: + /// Construction + TypeError(const char * sMessage); + TypeError(const std::string& sMessage); + /// Construction + TypeError(const TypeError &inst); + /// Destruction + virtual ~TypeError() throw() {} +}; + +/** + * The ValueError can be used to indicate the usage of a wrong value. + * @author Werner Mayer + */ +class BaseExport ValueError : public Exception +{ +public: + /// Construction + ValueError(const char * sMessage); + ValueError(const std::string& sMessage); + /// Construction + ValueError(const ValueError &inst); + /// Destruction + virtual ~ValueError() throw() {} +}; + +/** + * The AttributeError can be used to indicate the usage of a wrong value. + * @author Werner Mayer + */ +class BaseExport AttributeError : public Exception +{ +public: + /// Construction + AttributeError(const char * sMessage); + AttributeError(const std::string& sMessage); + /// Construction + AttributeError(const AttributeError &inst); + /// Destruction + virtual ~AttributeError() throw() {} +}; + +/** + * The RuntimeError can be used to indicate an unknown exception at runtime. + * @author Werner Mayer + */ +class BaseExport RuntimeError : public Exception +{ +public: + /// Construction + RuntimeError(const char * sMessage); + RuntimeError(const std::string& sMessage); + /// Construction + RuntimeError(const RuntimeError &inst); + /// Destruction + virtual ~RuntimeError() throw() {} +}; + inline void Exception::setMessage(const char * sMessage) { diff --git a/src/Base/FileInfo.cpp b/src/Base/FileInfo.cpp index 0309cda32..6e8119f0c 100644 --- a/src/Base/FileInfo.cpp +++ b/src/Base/FileInfo.cpp @@ -26,6 +26,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include @@ -51,10 +52,20 @@ #include #include -#define new DEBUG_CLIENTBLOCK - using namespace Base; +#ifndef R_OK +#define R_OK 4 /* Test for read permission */ +#endif +#ifndef W_OK +#define W_OK 2 /* Test for write permission */ +#endif +#ifndef X_OK +#define X_OK 1 /* Test for execute permission */ +#endif +#ifndef F_OK +#define F_OK 0 /* Test for existence */ +#endif //********************************************************************************** // helper @@ -188,22 +199,13 @@ std::string FileInfo::getTempFileName(const char* FileName, const char* Path) void FileInfo::setFile(const char* name) { - std::string result; - const char *It=name; - - while(*It != '\0') { - switch(*It) - { - case '\\': - result += "/"; - break; - default: - result += *It; - } - It++; + if (!name) { + FileName.clear(); + return; } - FileName = result; + FileName = name; + std::replace(FileName.begin(), FileName.end(), '\\', '/'); } std::string FileInfo::filePath () const @@ -271,9 +273,9 @@ bool FileInfo::exists () const { #if defined (FC_OS_WIN32) std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(),0) == 0; + return _waccess(wstr.c_str(),F_OK) == 0; #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(),0) == 0; + return access(FileName.c_str(),F_OK) == 0; #endif } @@ -281,9 +283,9 @@ bool FileInfo::isReadable () const { #if defined (FC_OS_WIN32) std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(),4) == 0; + return _waccess(wstr.c_str(),R_OK) == 0; #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(),4) == 0; + return access(FileName.c_str(),R_OK) == 0; #endif } @@ -291,9 +293,29 @@ bool FileInfo::isWritable () const { #if defined (FC_OS_WIN32) std::wstring wstr = toStdWString(); - return _waccess(wstr.c_str(),2) == 0; + return _waccess(wstr.c_str(),W_OK) == 0; #elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) - return access(FileName.c_str(),2) == 0; + return access(FileName.c_str(),W_OK) == 0; +#endif +} + +bool FileInfo::setPermissions (Permissions perms) +{ + bool ret = false; + int mode = 0; + + if (perms & FileInfo::ReadOnly) + mode |= S_IREAD; + if (perms & FileInfo::WriteOnly) + mode |= S_IWRITE; + + if (mode == 0) // bad argument + return false; +#if defined (FC_OS_WIN32) + std::wstring wstr = toStdWString(); + return _wchmod(wstr.c_str(),mode) == 0; +#elif defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) + return chmod(FileName.c_str(),mode) == 0; #endif } @@ -428,7 +450,6 @@ bool FileInfo::renameFile(const char* NewName) #else # error "FileInfo::renameFile() not implemented for this platform!" #endif - setFile(NewName); return res; } @@ -482,12 +503,17 @@ bool FileInfo::deleteDirectoryRecursive(void) const std::vector List = getDirectoryContent(); for (std::vector::iterator It = List.begin();It!=List.end();++It) { - if (It->isDir()) + if (It->isDir()) { + It->setPermissions(FileInfo::ReadWrite); It->deleteDirectoryRecursive(); - else if(It->isFile()) + } + else if (It->isFile()) { + It->setPermissions(FileInfo::ReadWrite); It->deleteFile(); - else + } + else { Base::Exception("FileInfo::deleteDirectoryRecursive(): Unknown object Type in directory!"); + } } return deleteDirectory(); } diff --git a/src/Base/FileInfo.h b/src/Base/FileInfo.h index f541dc216..52fd77548 100644 --- a/src/Base/FileInfo.h +++ b/src/Base/FileInfo.h @@ -42,6 +42,12 @@ namespace Base class BaseExport FileInfo { public: + enum Permissions { + WriteOnly = 0x01, + ReadOnly = 0x02, + ReadWrite = 0x03, + }; + /// Constrction FileInfo (const char* _FileName=""); FileInfo (const std::string &_FileName); @@ -89,6 +95,8 @@ public: bool isReadable () const; /// Checks if the file exist and is writable bool isWritable () const; + /// Tries to set the file permisson + bool setPermissions (Permissions); /// Checks if it is a file (not a direrctory) bool isFile () const; /// Checks if it is a directory (not a file) @@ -109,7 +117,7 @@ public: std::vector getDirectoryContent(void) const; /// Delete an empty directory bool deleteDirectory(void) const; - /// Delete a directory and all its content + /// Delete a directory and all its content. bool deleteDirectoryRecursive(void) const; //@} diff --git a/src/Base/Interpreter.cpp b/src/Base/Interpreter.cpp index b9676bb9b..548265d98 100644 --- a/src/Base/Interpreter.cpp +++ b/src/Base/Interpreter.cpp @@ -71,9 +71,18 @@ PyException::PyException(void) _stackTrace = PP_last_error_trace; /* exception traceback text */ +} +PyException::~PyException() throw() +{ + PyGILStateLocker locker; PyErr_Clear(); // must be called to keep Python interpreter in a valid state (Werner) +} +void PyException::ReportException (void) const +{ + Base::Console().Error("%s%s: %s\n", + _stackTrace.c_str(), _errorType.c_str(), what()); } // --------------------------------------------------------- diff --git a/src/Base/Interpreter.h b/src/Base/Interpreter.h index a055e8aa2..fb61ccb73 100644 --- a/src/Base/Interpreter.h +++ b/src/Base/Interpreter.h @@ -54,11 +54,12 @@ class BaseExport PyException : public Exception public: /// constructor does the whole job PyException(void); - ~PyException() throw() {} + ~PyException() throw(); /// this function returns the stack trace const std::string &getStackTrace(void) const {return _stackTrace;} const std::string &getErrorType(void) const {return _errorType;} + void ReportException (void) const; protected: std::string _stackTrace; diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index 159952c7e..2cf30426c 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -38,9 +38,16 @@ using namespace Base; std::string RotationPy::representation(void) const { RotationPy::PointerType ptr = reinterpret_cast(_pcTwinPointer); + Py::Float q0(ptr->getValue()[0]); + Py::Float q1(ptr->getValue()[1]); + Py::Float q2(ptr->getValue()[2]); + Py::Float q3(ptr->getValue()[3]); std::stringstream str; - str << "Quaternion ("; - str << ptr->getValue()[0] << ","<< ptr->getValue()[1] << "," << ptr->getValue()[2] << "," << ptr->getValue()[3]; + str << "Rotation ("; + str << (std::string)q0.repr() << ", " + << (std::string)q1.repr() << ", " + << (std::string)q2.repr() << ", " + << (std::string)q3.repr(); str << ")"; return str.str(); diff --git a/src/Base/VectorPyImp.cpp b/src/Base/VectorPyImp.cpp index da2bb5831..4a1bae849 100644 --- a/src/Base/VectorPyImp.cpp +++ b/src/Base/VectorPyImp.cpp @@ -40,9 +40,12 @@ using namespace Base; std::string VectorPy::representation(void) const { VectorPy::PointerType ptr = reinterpret_cast(_pcTwinPointer); + Py::Float x(ptr->x); + Py::Float y(ptr->y); + Py::Float z(ptr->z); std::stringstream str; str << "Vector ("; - str << ptr->x << ", "<< ptr->y << ", "<< ptr->z; + str << (std::string)x.repr() << ", "<< (std::string)y.repr() << ", "<< (std::string)z.repr(); str << ")"; return str.str(); diff --git a/src/Build/Version.h.in b/src/Build/Version.h.in index d76718751..954f3dcef 100644 --- a/src/Build/Version.h.in +++ b/src/Build/Version.h.in @@ -1,7 +1,7 @@ // Version Number #define FCVersionMajor "0" -#define FCVersionMinor "13" +#define FCVersionMinor "14" #define FCVersionName "Vulcan" // test: $Format:Hash (%H), Date: %ci$ #define FCRevision "$WCREV$" //Highest committed revision number diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 7af6737fb..1fe47f5c4 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -41,8 +41,11 @@ # include #endif # include +# include #endif +#include + // FreeCAD Base header #include @@ -336,6 +339,18 @@ Application::Application(bool GUIenabled) ("User parameter:BaseApp/Preferences/Units"); Base::UnitsApi::setDecimals(hUnits->GetInt("Decimals", Base::UnitsApi::getDecimals())); + // Check for the symbols for group separator and deciaml point. They must be different otherwise + // Qt doesn't work properly. +#if defined(Q_OS_WIN32) + if (QLocale::system().groupSeparator() == QLocale::system().decimalPoint()) { + QMessageBox::critical(0, QLatin1String("Invalid system settings"), + QLatin1String("Your system uses the same symbol for decimal point and group separator.\n\n" + "This causes serious problems and makes the application fail to work properly.\n" + "Go to the system configuration panel of the OS and fix this issue, please.")); + throw Base::Exception("Invalid system settings"); + } +#endif + // setting up Python binding Base::PyGILStateLocker lock; PyObject* module = Py_InitModule3("FreeCADGui", Application::Methods, @@ -1739,9 +1754,24 @@ void Application::runApplication(void) Base::Console().Log("Init: Entering event loop\n"); try { + std::stringstream s; + s << Base::FileInfo::getTempPath() << App::GetApplication().getExecutableName() + << "_" << QCoreApplication::applicationPid() << ".lock"; + // open a lock file with the PID + Base::FileInfo fi(s.str()); + Base::ofstream lock(fi); + boost::interprocess::file_lock flock(s.str().c_str()); + flock.lock(); + int ret = mainApp.exec(); if (ret == systemExit) throw Base::SystemExitException(); + + // close the lock file, in case of a crash we can see the existing lock file + // on the next restart and try to repair the documents, if needed. + flock.unlock(); + lock.close(); + fi.deleteFile(); } catch (const Base::SystemExitException&) { Base::Console().Message("System exit\n"); @@ -1756,3 +1786,66 @@ void Application::runApplication(void) Base::Console().Log("Finish: Event loop left\n"); } + +void Application::checkForPreviousCrashes() +{ + QDir tmp = QDir::temp(); + tmp.setNameFilters(QStringList() << QString::fromAscii("*.lock")); + tmp.setFilter(QDir::Files); + + QList restoreDocFiles; + QString exeName = QString::fromAscii(App::GetApplication().getExecutableName()); + QList locks = tmp.entryInfoList(); + for (QList::iterator it = locks.begin(); it != locks.end(); ++it) { + QString bn = it->baseName(); + // ignore the lock file for this instance + QString pid = QString::number(QCoreApplication::applicationPid()); + if (bn.startsWith(exeName) && bn.indexOf(pid) < 0) { + QString fn = it->absoluteFilePath(); + boost::interprocess::file_lock flock((const char*)fn.toLocal8Bit()); + if (flock.try_lock()) { + // OK, this file is a leftover from a previous crash + QString crashed_pid = bn.mid(exeName.length()+1); + // search for transient directories with this PID + QString filter; + QTextStream str(&filter); + str << exeName << "_Doc_*_" << crashed_pid; + tmp.setNameFilters(QStringList() << filter); + tmp.setFilter(QDir::Dirs); + QList dirs = tmp.entryInfoList(); + if (dirs.isEmpty()) { + // delete the lock file immediately if not transient directories are related + tmp.remove(fn); + } + else { + int countDeletedDocs = 0; + for (QList::iterator it = dirs.begin(); it != dirs.end(); ++it) { + QDir doc_dir(it->absoluteFilePath()); + doc_dir.setFilter(QDir::NoDotAndDotDot|QDir::AllEntries); + uint entries = doc_dir.entryList().count(); + if (entries == 0) { + // in this case we can delete the transient directory because + // we cannot do anything + if (tmp.rmdir(it->filePath())) + countDeletedDocs++; + } + else { + // store the transient directory in case it's not empty + restoreDocFiles << *it; + } + } + + // all directories corresponding to the lock file have been deleted + // so delete the lock file, too + if (countDeletedDocs == dirs.size()) { + tmp.remove(fn); + } + } + } + } + } + + if (!restoreDocFiles.isEmpty()) { + //TODO: + } +} diff --git a/src/Gui/Application.h b/src/Gui/Application.h index ad70000a6..8d4aefe5a 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -157,6 +157,7 @@ public: /// true when the application shuting down bool isClosing(void); + void checkForPreviousCrashes(); /** @name workbench handling */ //@{ @@ -210,6 +211,7 @@ public: PYFUNCDEF_S(sSendActiveView); + PYFUNCDEF_S(sGetMainWindow); PYFUNCDEF_S(sUpdateGui); PYFUNCDEF_S(sUpdateLocale); PYFUNCDEF_S(sGetLocale); diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 31df22271..fb3de1211 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -83,6 +83,9 @@ PyMethodDef Application::Methods[] = { {"addIcon", (PyCFunction) Application::sAddIcon, 1, "addIcon(string, string or list) -> None\n\n" "Add an icon as file name or in XPM format to the system"}, + {"getMainWindow", (PyCFunction) Application::sGetMainWindow, 1, + "getMainWindow() -> QMainWindow\n\n" + "Return the main window instance"}, {"updateGui", (PyCFunction) Application::sUpdateGui, 1, "updateGui() -> None\n\n" "Update the main window and all its windows"}, @@ -418,6 +421,22 @@ PyObject* Application::sSendActiveView(PyObject * /*self*/, PyObject *args,PyObj return Py_None; } +PyObject* Application::sGetMainWindow(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + PythonWrapper wrap; + wrap.loadCoreModule(); + wrap.loadGuiModule(); + try { + return Py::new_reference_to(wrap.fromQWidget(Gui::getMainWindow(), "QMainWindow")); + } + catch (const Py::Exception&) { + return 0; + } +} + PyObject* Application::sUpdateGui(PyObject * /*self*/, PyObject *args,PyObject * /*kwd*/) { if (!PyArg_ParseTuple(args, "")) // convert args: Python->C diff --git a/src/Gui/BlenderNavigationStyle.cpp b/src/Gui/BlenderNavigationStyle.cpp index 2b782a19f..1769cdf90 100644 --- a/src/Gui/BlenderNavigationStyle.cpp +++ b/src/Gui/BlenderNavigationStyle.cpp @@ -84,7 +84,7 @@ SbBool BlenderNavigationStyle::processSoEvent(const SoEvent * const ev) // up the inheritance hierarchy. if (this->isSeekMode()) { return inherited::processSoEvent(ev); } // Switch off viewing mode (Bug #0000911) - if (!this->isSeekMode() && this->isViewing()) + if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) this->setViewing(false); // by default disable viewing mode to render the scene const SoType type(ev->getTypeId()); diff --git a/src/Gui/CADNavigationStyle.cpp b/src/Gui/CADNavigationStyle.cpp index 0aded9a73..8b3687bbe 100644 --- a/src/Gui/CADNavigationStyle.cpp +++ b/src/Gui/CADNavigationStyle.cpp @@ -86,7 +86,7 @@ SbBool CADNavigationStyle::processSoEvent(const SoEvent * const ev) if (this->isSeekMode()) { return inherited::processSoEvent(ev); } #else // Switch off viewing mode (Bug #0000911) - if (!this->isSeekMode() && this->isViewing()) + if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) this->setViewing(false); // by default disable viewing mode to render the scene #endif diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 166dd4830..8ecf08ac0 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -3,6 +3,7 @@ if(WIN32) add_definitions(-DFCGui -DQIIS_MAKEDLL) endif(WIN32) + if (FREECAD_USE_3DCONNEXION) add_definitions(-D_USE_3DCONNEXION_SDK) endif(FREECAD_USE_3DCONNEXION) @@ -59,6 +60,30 @@ IF(SPNAV_FOUND) ) ENDIF(SPNAV_FOUND) +if(SHIBOKEN_INCLUDE_DIR) + add_definitions(-DHAVE_SHIBOKEN) + include_directories( + ${SHIBOKEN_INCLUDE_DIR} + ) + set(FreeCADGui_LIBS + ${FreeCADGui_LIBS} + ${SHIBOKEN_LIBRARY} + ) +endif(SHIBOKEN_INCLUDE_DIR) + +if(PYSIDE_INCLUDE_DIR) + add_definitions(-DHAVE_PYSIDE) + include_directories( + ${PYSIDE_INCLUDE_DIR} + ${PYSIDE_INCLUDE_DIR}/QtCore + ${PYSIDE_INCLUDE_DIR}/QtGui + ) + set(FreeCADGui_LIBS + ${FreeCADGui_LIBS} + ${PYSIDE_LIBRARY} + ) +endif(PYSIDE_INCLUDE_DIR) + generate_from_xml(DocumentPy) generate_from_xml(PythonWorkbenchPy) generate_from_xml(ViewProviderPy) @@ -97,8 +122,11 @@ set(Gui_MOC_HDRS CallTips.h CombiView.h Control.h + Clipping.h DemoMode.h DownloadDialog.h + DownloadItem.h + DownloadManager.h DlgActionsImp.h DlgActivateWindowImp.h DlgCommandsImp.h @@ -186,6 +214,7 @@ fc_wrap_cpp(Gui_MOC_SRCS ${Gui_MOC_HDRS}) SET(Gui_UIC_SRCS AboutApplication.ui + Clipping.ui DemoMode.ui DlgActions.ui DlgActivateWindow.ui @@ -220,6 +249,8 @@ SET(Gui_UIC_SRCS DlgTreeWidget.ui DlgLocationAngle.ui DlgLocationPos.ui + DownloadManager.ui + DownloadItem.ui MouseButtons.ui SceneInspector.ui InputVector.ui @@ -260,6 +291,7 @@ SOURCE_GROUP("Command" FILES ${Command_SRCS}) # The dialog sources SET(Dialog_CPP_SRCS + Clipping.cpp DemoMode.cpp DlgActivateWindowImp.cpp DlgDisplayPropertiesImp.cpp @@ -282,9 +314,12 @@ SET(Dialog_CPP_SRCS TextureMapping.cpp Transform.cpp DownloadDialog.cpp + DownloadItem.cpp + DownloadManager.cpp ) SET(Dialog_HPP_SRCS + Clipping.h DemoMode.h DlgActivateWindowImp.h DlgDisplayPropertiesImp.h @@ -307,17 +342,22 @@ SET(Dialog_HPP_SRCS TextureMapping.h Transform.h DownloadDialog.h + DownloadItem.h + DownloadManager.h ) SET(Dialog_SRCS ${Dialog_CPP_SRCS} ${Dialog_HPP_SRCS} AboutApplication.ui + Clipping.ui DemoMode.ui DlgActivateWindow.ui DlgAuthorization.ui DlgDisplayProperties.ui DlgInputDialog.ui + DlgLocationAngle.ui + DlgLocationPos.ui DlgMacroExecute.ui DlgRunExternal.ui DlgMacroRecord.ui @@ -327,6 +367,8 @@ SET(Dialog_SRCS DlgProjectUtility.ui DlgTipOfTheDay.ui DlgTreeWidget.ui + DownloadManager.ui + DownloadItem.ui MouseButtons.ui InputVector.ui Placement.ui @@ -884,7 +926,13 @@ else(WIN32) INSTALL(TARGETS FreeCADGui LIBRARY DESTINATION lib ) - INSTALL(FILES Icons/freecad.xpm Icons/freecad-doc.png + INSTALL(FILES Icons/freecad.xpm + Icons/freecad-icon-16.png + Icons/freecad-icon-32.png + Icons/freecad-icon-48.png + Icons/freecad-icon-64.png + Icons/freecad.svg + Icons/freecad-doc.png DESTINATION ${CMAKE_INSTALL_DATADIR} ) endif(WIN32) diff --git a/src/Gui/Clipping.cpp b/src/Gui/Clipping.cpp new file mode 100644 index 000000000..7aacc7a37 --- /dev/null +++ b/src/Gui/Clipping.cpp @@ -0,0 +1,315 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +#endif +# include + +#include "Clipping.h" +#include "ui_Clipping.h" +#include "Application.h" +#include "View3DInventor.h" +#include "View3DInventorViewer.h" + +using namespace Gui::Dialog; + +class Clipping::Private { +public: + Ui_Clipping ui; + QPointer view; + SoGroup* node; + SoClipPlane* clipX; + SoClipPlane* clipY; + SoClipPlane* clipZ; + SoClipPlane* clipView; + bool flipX; + bool flipY; + bool flipZ; + SoTimerSensor* sensor; + Private() : flipX(false), flipY(false), flipZ(false) + { + clipX = new SoClipPlane(); + clipX->on.setValue(false); + clipX->plane.setValue(SbPlane(SbVec3f(1,0,0),0)); + clipX->ref(); + + clipY = new SoClipPlane(); + clipY->on.setValue(false); + clipY->plane.setValue(SbPlane(SbVec3f(0,1,0),0)); + clipY->ref(); + + clipZ = new SoClipPlane(); + clipZ->on.setValue(false); + clipZ->plane.setValue(SbPlane(SbVec3f(0,0,1),0)); + clipZ->ref(); + + clipView = new SoClipPlane(); + clipView->on.setValue(false); + clipView->plane.setValue(SbPlane(SbVec3f(0,0,1),0)); + clipView->ref(); + + sensor = new SoTimerSensor(moveCallback, this); + } + ~Private() + { + clipX->unref(); + clipY->unref(); + clipZ->unref(); + clipView->unref(); + delete sensor; + } + static void moveCallback(void * data, SoSensor * sensor) + { + Private* self = reinterpret_cast(data); + if (self->view) { + Gui::View3DInventorViewer* view = self->view->getViewer(); + SoClipPlane* clip = self->clipView; + SbPlane pln = clip->plane.getValue(); + clip->plane.setValue(SbPlane(view->getViewDirection(),pln.getDistanceFromOrigin())); + } + } +}; + +/* TRANSLATOR Gui::Dialog::Clipping */ + +Clipping::Clipping(Gui::View3DInventor* view, QWidget* parent) + : QWidget(parent), d(new Private) +{ + // create widgets + d->ui.setupUi(this); + d->ui.clipView->setRange(-INT_MAX,INT_MAX); + d->ui.clipView->setSingleStep(0.1f); + d->ui.clipX->setRange(-INT_MAX,INT_MAX); + d->ui.clipX->setSingleStep(0.1f); + d->ui.clipY->setRange(-INT_MAX,INT_MAX); + d->ui.clipY->setSingleStep(0.1f); + d->ui.clipZ->setRange(-INT_MAX,INT_MAX); + d->ui.clipZ->setSingleStep(0.1f); + + d->ui.dirX->setRange(-INT_MAX,INT_MAX); + d->ui.dirX->setSingleStep(0.1f); + d->ui.dirY->setRange(-INT_MAX,INT_MAX); + d->ui.dirY->setSingleStep(0.1f); + d->ui.dirZ->setRange(-INT_MAX,INT_MAX); + d->ui.dirZ->setSingleStep(0.1f); + d->ui.dirZ->setValue(1.0f); + + d->view = view; + View3DInventorViewer* viewer = view->getViewer(); + d->node = static_cast(viewer->getSceneGraph()); + d->node->ref(); + d->node->insertChild(d->clipX, 0); + d->node->insertChild(d->clipY, 0); + d->node->insertChild(d->clipZ, 0); + d->node->insertChild(d->clipView, 0); + + SoGetBoundingBoxAction action(viewer->getViewportRegion()); + action.apply(viewer->getSceneGraph()); + SbBox3f box = action.getBoundingBox(); + + if (!box.isEmpty()) { + SbVec3f cnt = box.getCenter(); + d->ui.clipView->setValue(cnt[2]); + d->ui.clipX->setValue(cnt[0]); + d->ui.clipY->setValue(cnt[1]); + d->ui.clipZ->setValue(cnt[2]); + } +} + +/** Destroys the object and frees any allocated resources */ +Clipping::~Clipping() +{ + d->node->removeChild(d->clipX); + d->node->removeChild(d->clipY); + d->node->removeChild(d->clipZ); + d->node->removeChild(d->clipView); + d->node->unref(); + delete d; +} + +void Clipping::on_groupBoxX_toggled(bool on) +{ + if (on) { + d->ui.groupBoxView->setChecked(false); + } + + d->clipX->on.setValue(on); +} + +void Clipping::on_groupBoxY_toggled(bool on) +{ + if (on) { + d->ui.groupBoxView->setChecked(false); + } + + d->clipY->on.setValue(on); +} + +void Clipping::on_groupBoxZ_toggled(bool on) +{ + if (on) { + d->ui.groupBoxView->setChecked(false); + } + + d->clipZ->on.setValue(on); +} + +void Clipping::on_clipX_valueChanged(double val) +{ + SbPlane pln = d->clipX->plane.getValue(); + d->clipX->plane.setValue(SbPlane(pln.getNormal(),d->flipX ? -val : val)); +} + +void Clipping::on_clipY_valueChanged(double val) +{ + SbPlane pln = d->clipY->plane.getValue(); + d->clipY->plane.setValue(SbPlane(pln.getNormal(),d->flipY ? -val : val)); +} + +void Clipping::on_clipZ_valueChanged(double val) +{ + SbPlane pln = d->clipZ->plane.getValue(); + d->clipZ->plane.setValue(SbPlane(pln.getNormal(),d->flipZ ? -val : val)); +} + +void Clipping::on_flipClipX_clicked() +{ + d->flipX = !d->flipX; + SbPlane pln = d->clipX->plane.getValue(); + d->clipX->plane.setValue(SbPlane(-pln.getNormal(),-pln.getDistanceFromOrigin())); +} + +void Clipping::on_flipClipY_clicked() +{ + d->flipY = !d->flipY; + SbPlane pln = d->clipY->plane.getValue(); + d->clipY->plane.setValue(SbPlane(-pln.getNormal(),-pln.getDistanceFromOrigin())); +} + +void Clipping::on_flipClipZ_clicked() +{ + d->flipZ = !d->flipZ; + SbPlane pln = d->clipZ->plane.getValue(); + d->clipZ->plane.setValue(SbPlane(-pln.getNormal(),-pln.getDistanceFromOrigin())); +} + +void Clipping::on_groupBoxView_toggled(bool on) +{ + if (on) { + d->ui.groupBoxX->setChecked(false); + d->ui.groupBoxY->setChecked(false); + d->ui.groupBoxZ->setChecked(false); + } + + d->clipView->on.setValue(on); +} + +void Clipping::on_clipView_valueChanged(double val) +{ + SbPlane pln = d->clipView->plane.getValue(); + d->clipView->plane.setValue(SbPlane(pln.getNormal(),val)); +} + +void Clipping::on_fromView_clicked() +{ + if (d->view) { + Gui::View3DInventorViewer* view = d->view->getViewer(); + SbVec3f dir = view->getViewDirection(); + SbPlane pln = d->clipView->plane.getValue(); + d->clipView->plane.setValue(SbPlane(dir,pln.getDistanceFromOrigin())); + } +} + +void Clipping::on_adjustViewdirection_toggled(bool on) +{ + d->ui.dirX->setDisabled(on); + d->ui.dirY->setDisabled(on); + d->ui.dirZ->setDisabled(on); + d->ui.fromView->setDisabled(on); + + if (on) + d->sensor->schedule(); + else + d->sensor->unschedule(); +} + +void Clipping::on_dirX_valueChanged(double) +{ + double x = d->ui.dirX->value(); + double y = d->ui.dirY->value(); + double z = d->ui.dirZ->value(); + + SbPlane pln = d->clipView->plane.getValue(); + SbVec3f normal(x,y,z); + if (normal.sqrLength() > 0.0f) + d->clipView->plane.setValue(SbPlane(normal,pln.getDistanceFromOrigin())); +} + +void Clipping::on_dirY_valueChanged(double) +{ + double x = d->ui.dirX->value(); + double y = d->ui.dirY->value(); + double z = d->ui.dirZ->value(); + + SbPlane pln = d->clipView->plane.getValue(); + SbVec3f normal(x,y,z); + if (normal.sqrLength() > 0.0f) + d->clipView->plane.setValue(SbPlane(normal,pln.getDistanceFromOrigin())); +} + +void Clipping::on_dirZ_valueChanged(double) +{ + double x = d->ui.dirX->value(); + double y = d->ui.dirY->value(); + double z = d->ui.dirZ->value(); + + SbPlane pln = d->clipView->plane.getValue(); + SbVec3f normal(x,y,z); + if (normal.sqrLength() > 0.0f) + d->clipView->plane.setValue(SbPlane(normal,pln.getDistanceFromOrigin())); +} + +// --------------------------------------- + +/* TRANSLATOR Gui::Dialog::TaskClipping */ + +TaskClipping::TaskClipping(Gui::View3DInventor* view) +{ + QWidget* widget = new Clipping(view); + Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox( + QPixmap(), widget->windowTitle(), false, 0); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +TaskClipping::~TaskClipping() +{ + // automatically deleted in the sub-class +} + +#include "moc_Clipping.cpp" diff --git a/src/Gui/Clipping.h b/src/Gui/Clipping.h new file mode 100644 index 000000000..67b0fa079 --- /dev/null +++ b/src/Gui/Clipping.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_DIALOG_CLIPPING_H +#define GUI_DIALOG_CLIPPING_H + +#include +#include +#include + +namespace Gui { +class View3DInventor; +namespace Dialog { + +/** + * @author Werner Mayer + */ +class GuiExport Clipping : public QWidget +{ + Q_OBJECT + +public: + Clipping(Gui::View3DInventor* view, QWidget* parent = 0); + ~Clipping(); + +protected Q_SLOTS: + void on_groupBoxX_toggled(bool); + void on_groupBoxY_toggled(bool); + void on_groupBoxZ_toggled(bool); + void on_clipX_valueChanged(double); + void on_clipY_valueChanged(double); + void on_clipZ_valueChanged(double); + void on_flipClipX_clicked(); + void on_flipClipY_clicked(); + void on_flipClipZ_clicked(); + void on_groupBoxView_toggled(bool); + void on_clipView_valueChanged(double); + void on_fromView_clicked(); + void on_adjustViewdirection_toggled(bool); + void on_dirX_valueChanged(double); + void on_dirY_valueChanged(double); + void on_dirZ_valueChanged(double); + +private: + +private: + class Private; + Private* d; +}; + +/** + * Embed the panel into a task dialog. + */ +class TaskClipping : public Gui::TaskView::TaskDialog +{ +public: + TaskClipping(Gui::View3DInventor* view); + ~TaskClipping(); + +public: + virtual QDialogButtonBox::StandardButtons getStandardButtons() const + { return QDialogButtonBox::Close; } +}; + +} // namespace Dialog +} // namespace Gui + +#endif // GUI_DIALOG_CLIPPING_H diff --git a/src/Gui/Clipping.ui b/src/Gui/Clipping.ui new file mode 100644 index 000000000..3cb877568 --- /dev/null +++ b/src/Gui/Clipping.ui @@ -0,0 +1,244 @@ + + + Gui::Dialog::Clipping + + + + 0 + 0 + 304 + 430 + + + + Clipping + + + + + + Clipping X + + + true + + + false + + + + + + + + + Flip + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Offset + + + + + + + + + + Clipping Y + + + true + + + false + + + + + + + + + Flip + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Offset + + + + + + + + + + Clipping Z + + + true + + + false + + + + + + + + + Flip + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Offset + + + + + + + + + + Clipping custom direction + + + true + + + false + + + + + + Qt::Horizontal + + + + 84 + 20 + + + + + + + + View + + + + + + + Adjust to view direction + + + + + + + Direction + + + + + + + + + + + + + + + + + + Offset + + + + + + + + + + + + + groupBoxX + clipX + flipClipX + groupBoxY + clipY + flipClipY + groupBoxZ + clipZ + flipClipZ + groupBoxView + clipView + fromView + adjustViewdirection + dirX + dirY + dirZ + + + + diff --git a/src/Gui/Command.cpp b/src/Gui/Command.cpp index 2fc6f8748..f1c4439d2 100644 --- a/src/Gui/Command.cpp +++ b/src/Gui/Command.cpp @@ -581,6 +581,12 @@ void Command::applyCommandData(Action* action) action->setWhatsThis(QCoreApplication::translate( this->className(), sToolTipText, 0, QCoreApplication::UnicodeUTF8)); + QString accel = action->shortcut().toString(); + if (!accel.isEmpty()) { + QString tip = QString::fromAscii("(%1)\t%2") + .arg(accel).arg(action->statusTip()); + action->setStatusTip(tip); + } } const char* Command::keySequenceToAccel(int sk) const diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index 86d7c2abc..65c705f78 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -841,7 +841,6 @@ void StdCmdCopy::activated(int iMsg) { bool done = getGuiApplication()->sendMsgToActiveView("Copy"); if (!done) { - WaitCursor wc; QMimeData * mimeData = getMainWindow()->createMimeDataFromSelection(); QClipboard* cb = QApplication::clipboard(); cb->setMimeData(mimeData); @@ -910,59 +909,54 @@ StdCmdDuplicateSelection::StdCmdDuplicateSelection() void StdCmdDuplicateSelection::activated(int iMsg) { - App::Document* act = App::GetApplication().getActiveDocument(); - if (!act) - return; // no active document found - Gui::Document* doc = Gui::Application::Instance->getDocument(act); - std::vector sel = Gui::Selection().getCompleteSelection(); + std::vector sel = Selection().getCompleteSelection(); + std::map< App::Document*, std::vector > objs; for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { - if (!it->pObject) - continue; // should actually not happen - // create a copy of the object - App::DocumentObject* copy = act->copyObject(it->pObject, false); - if (!copy) // continue if no copy could be created - continue; - // mark all properties of the copy as "touched" which are touched in the original object - std::map props; - it->pObject->getPropertyMap(props); - std::map copy_props; - copy->getPropertyMap(copy_props); - for (std::map::iterator jt = props.begin(); jt != props.end(); ++jt) { - if (jt->second->isTouched()) { - std::map::iterator kt; - kt = copy_props.find(jt->first); - if (kt != copy_props.end()) { - kt->second->touch(); - } - } - } - - Gui::Document* parent = Gui::Application::Instance->getDocument(it->pObject->getDocument()); - if (!parent || !doc) - continue; // should not happen - // copy the properties of the associated view providers - Gui::ViewProvider* view = parent->getViewProvider(it->pObject); - Gui::ViewProvider* copy_view = doc->getViewProvider(copy); - copy_view->addDynamicProperties(view); - if (!view || !copy_view) - continue; // should not happen - - // get the properties of the view provider - props.clear(); - view->getPropertyMap(props); - copy_props.clear(); - copy_view->getPropertyMap(copy_props); - for (std::map::iterator jt = props.begin(); jt != props.end(); ++jt) { - std::map::iterator kt; - kt = copy_props.find(jt->first); - if (kt != copy_props.end()) { - std::auto_ptr data(jt->second->Copy()); - if (data.get()) { - kt->second->Paste(*data); - } - } + if (it->pObject && it->pObject->getDocument()) { + objs[it->pObject->getDocument()].push_back(it->pObject); } } + + if (objs.empty()) + return; + + Base::FileInfo fi(Base::FileInfo::getTempFileName()); + { + std::vector sel; // selected + std::vector all; // object sub-graph + for (std::map< App::Document*, std::vector >::iterator it = objs.begin(); it != objs.end(); ++it) { + std::vector dep = it->first->getDependencyList(it->second); + sel.insert(sel.end(), it->second.begin(), it->second.end()); + all.insert(all.end(), dep.begin(), dep.end()); + } + + if (all.size() > sel.size()) { + int ret = QMessageBox::question(getMainWindow(), + qApp->translate("Std_DuplicateSelection","Object dependencies"), + qApp->translate("Std_DuplicateSelection","The selected objects have a dependency to unselected objects.\n" + "Do you want to duplicate them, too?"), + QMessageBox::Yes,QMessageBox::No); + if (ret == QMessageBox::Yes) { + sel = all; + } + } + + // save stuff to file + Base::ofstream str(fi, std::ios::out | std::ios::binary); + App::Document* doc = sel.front()->getDocument(); + MergeDocuments mimeView(doc); + doc->exportObjects(sel, str); + str.close(); + } + App::Document* doc = App::GetApplication().getActiveDocument(); + if (doc) { + // restore objects from file and add to active document + Base::ifstream str(fi, std::ios::in | std::ios::binary); + MergeDocuments mimeView(doc); + mimeView.importObjects(str); + str.close(); + } + fi.deleteFile(); } bool StdCmdDuplicateSelection::isActive(void) @@ -1032,17 +1026,52 @@ void StdCmdDelete::activated(int iMsg) Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(*it); std::vector sel = rSel.getSelectionEx((*it)->getName()); if (!sel.empty()) { - (*it)->openTransaction("Delete"); + bool doDeletion = true; + // check if we can delete the object for (std::vector::iterator ft = sel.begin(); ft != sel.end(); ++ft) { + App::DocumentObject* obj = ft->getObject(); Gui::ViewProvider* vp = pGuiDoc->getViewProvider(ft->getObject()); - if (vp) { - // ask the ViewProvider if its want to do some clean up - if (vp->onDelete(ft->getSubNames())) - doCommand(Doc,"App.getDocument(\"%s\").removeObject(\"%s\")" - ,(*it)->getName(), ft->getFeatName()); + // if the object is in edit mode we allow to continue because only sub-elements will be removed + if (!vp || !vp->isEditing()) { + std::vector links = obj->getInList(); + if (!links.empty()) { + // check if the referenced objects are groups or are selected too + for (std::vector::iterator lt = links.begin(); lt != links.end(); ++lt) { + if (!(*lt)->getTypeId().isDerivedFrom(App::DocumentObjectGroup::getClassTypeId()) && !rSel.isSelected(*lt)) { + doDeletion = false; + break; + } + } + + if (!doDeletion) { + break; + } + } } } - (*it)->commitTransaction(); + + if (!doDeletion) { + int ret = QMessageBox::question(Gui::getMainWindow(), + qApp->translate("Std_Delete", "Object dependencies"), + qApp->translate("Std_Delete", "This object is referenced by other objects and thus these objects might get broken.\n" + "Are you sure to continue?"), + QMessageBox::Yes, QMessageBox::No); + if (ret == QMessageBox::Yes) + doDeletion = true; + } + if (doDeletion) { + (*it)->openTransaction("Delete"); + for (std::vector::iterator ft = sel.begin(); ft != sel.end(); ++ft) { + Gui::ViewProvider* vp = pGuiDoc->getViewProvider(ft->getObject()); + if (vp) { + // ask the ViewProvider if it wants to do some clean up + if (vp->onDelete(ft->getSubNames())) + doCommand(Doc,"App.getDocument(\"%s\").removeObject(\"%s\")" + ,(*it)->getName(), ft->getFeatName()); + } + } + (*it)->commitTransaction(); + } } } } diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index 4b2d5b410..ee441c695 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -38,6 +38,7 @@ #include "Application.h" #include "BitmapFactory.h" #include "Control.h" +#include "Clipping.h" #include "FileDialog.h" #include "MainWindow.h" #include "Tree.h" @@ -49,6 +50,7 @@ #include "Selection.h" #include "SoFCOffscreenRenderer.h" #include "SoFCBoundingBox.h" +#include "SoFCUnifiedSelection.h" #include "SoAxisCrossKit.h" #include "View3DInventor.h" #include "View3DInventorViewer.h" @@ -473,12 +475,15 @@ StdCmdToggleClipPlane::StdCmdToggleClipPlane() Action * StdCmdToggleClipPlane::createAction(void) { Action *pcAction = (Action*)Command::createAction(); +#if 0 pcAction->setCheckable(true); +#endif return pcAction; } void StdCmdToggleClipPlane::activated(int iMsg) { +#if 0 View3DInventor* view = qobject_cast(getMainWindow()->activeWindow()); if (view) { if (iMsg > 0 && !view->hasClippingPlane()) @@ -486,10 +491,17 @@ void StdCmdToggleClipPlane::activated(int iMsg) else if (iMsg == 0 && view->hasClippingPlane()) view->toggleClippingPlane(); } +#else + View3DInventor* view = qobject_cast(getMainWindow()->activeWindow()); + if (view) { + Gui::Control().showDialog(new Gui::Dialog::TaskClipping(view)); + } +#endif } bool StdCmdToggleClipPlane::isActive(void) { +#if 0 View3DInventor* view = qobject_cast(getMainWindow()->activeWindow()); if (view) { Action* action = qobject_cast(_pcAction); @@ -503,6 +515,11 @@ bool StdCmdToggleClipPlane::isActive(void) action->setChecked(false); return false; } +#else + if (Gui::Control().activeDialog()) + return false; + return true; +#endif } DEF_STD_CMD_ACL(StdCmdDrawStyle); @@ -1922,6 +1939,9 @@ static void selectionCallback(void * ud, SoEventCallback * cb) { Gui::View3DInventorViewer* view = reinterpret_cast(cb->getUserData()); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback, ud); + SoNode* root = view->getSceneGraph(); + static_cast(root)->selectionRole.setValue(TRUE); + std::vector picked = view->getGLPolygon(); SoCamera* cam = view->getCamera(); SbViewVolume vv = cam->getViewVolume(); @@ -1972,8 +1992,10 @@ void StdBoxSelection::activated(int iMsg) if (view) { View3DInventorViewer* viewer = view->getViewer(); if (!viewer->isSelecting()) { - viewer->startSelection(View3DInventorViewer::Rectangle); + viewer->startSelection(View3DInventorViewer::Rubberband); viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback); + SoNode* root = viewer->getSceneGraph(); + static_cast(root)->selectionRole.setValue(FALSE); } } } diff --git a/src/Gui/DemoMode.cpp b/src/Gui/DemoMode.cpp index 2d36af572..50217352e 100644 --- a/src/Gui/DemoMode.cpp +++ b/src/Gui/DemoMode.cpp @@ -26,6 +26,7 @@ # include # include # include +# include # include #include #endif @@ -53,6 +54,11 @@ DemoMode::DemoMode(QWidget* parent, Qt::WFlags fl) timer->setInterval(1000 * ui->timeout->value()); connect(timer, SIGNAL(timeout()), this, SLOT(onAutoPlay())); oldvalue = ui->angleSlider->value(); + + wasHidden = false; + showHideTimer = new QTimer(this); + showHideTimer->setInterval(5000); + connect(showHideTimer, SIGNAL(timeout()), this, SLOT(hide())); } /** Destroys the object and frees any allocated resources */ @@ -82,6 +88,35 @@ void DemoMode::reject() QDialog::reject(); } +bool DemoMode::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::MouseMove) { + if (ui->fullscreen->isChecked()) { + QPoint point = QCursor::pos() - oldPos; + if (point.manhattanLength() > 5) { + show(); + showHideTimer->start(); + } + } + } + return QDialog::eventFilter(obj, event); +} + +void DemoMode::showEvent(QShowEvent *) +{ + if (this->wasHidden) + this->move(this->pnt); + this->wasHidden = false; +} + +void DemoMode::hideEvent(QHideEvent *) +{ + this->pnt = this->pos(); + this->wasHidden = true; + this->oldPos = QCursor::pos(); + showHideTimer->stop(); +} + Gui::View3DInventor* DemoMode::activeView() const { Document* doc = Application::Instance->activeDocument(); @@ -186,6 +221,14 @@ void DemoMode::on_fullscreen_toggled(bool on) view->setCurrentViewMode(on ? MDIView::/*TopLevel*/FullScreen : MDIView::Child); this->activateWindow(); } + if (on) { + qApp->installEventFilter(this); + showHideTimer->start(); + } + else { + qApp->removeEventFilter(this); + showHideTimer->stop(); + } } void DemoMode::on_timeout_valueChanged(int v) diff --git a/src/Gui/DemoMode.h b/src/Gui/DemoMode.h index 0c047005c..1f4fb67ea 100644 --- a/src/Gui/DemoMode.h +++ b/src/Gui/DemoMode.h @@ -68,12 +68,19 @@ private: Gui::View3DInventor* activeView() const; void startAnimation(Gui::View3DInventor*); void changeEvent(QEvent *e); + bool eventFilter(QObject *, QEvent *); + void showEvent(QShowEvent *); + void hideEvent(QHideEvent *); private: int oldvalue; SbVec3f viewAxis; + bool wasHidden; + QPoint pnt; + QPoint oldPos; Ui_DemoMode* ui; QTimer* timer; + QTimer* showHideTimer; }; } // namespace Dialog diff --git a/src/Gui/DlgAuthorization.ui b/src/Gui/DlgAuthorization.ui index 35fb1e567..afbed6e51 100644 --- a/src/Gui/DlgAuthorization.ui +++ b/src/Gui/DlgAuthorization.ui @@ -1,118 +1,55 @@ - - - - + + Gui::Dialog::DlgAuthorization - - + + 0 0 - 304 - 189 + 284 + 128 - + Authorization - + true - - - 11 - - - 6 - - - - - 0 - - - 6 - - - - - &OK - - - - - - true - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 140 - 20 - - - - - - - - &Cancel - - - - - - true - - - - - - - - - QLineEdit::Password + + + + + Username: - - - + + + + + + Password: - - - - - - - User name: + + + + QLineEdit::Password - + - + Qt::Vertical - + QSizePolicy::Expanding - + 21 41 @@ -120,48 +57,43 @@ + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Site: + + + + + + + + 75 + true + + + + %1 at %2 + + + true + + + - + username password - buttonOk - buttonCancel - - - buttonOk - clicked() - Gui::Dialog::DlgAuthorization - accept() - - - 20 - 20 - - - 20 - 20 - - - - - buttonCancel - clicked() - Gui::Dialog::DlgAuthorization - reject() - - - 20 - 20 - - - 20 - 20 - - - - + diff --git a/src/Gui/DlgProjectInformation.ui b/src/Gui/DlgProjectInformation.ui index 60b0378d1..f080e6233 100644 --- a/src/Gui/DlgProjectInformation.ui +++ b/src/Gui/DlgProjectInformation.ui @@ -40,9 +40,6 @@ 6 - - - @@ -56,32 +53,16 @@ - - - - Qt::Vertical - - - QSizePolicy::Expanding - - + + + - 91 - 240 + 0 + 25 - - - - - - Commen&t: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - textEditComment + + true @@ -108,40 +89,30 @@ - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - + + - &Last modified by: + UUID: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - lineEditLastMod + + + + + + + 0 + 25 + + + + true - + Created &by: @@ -154,20 +125,66 @@ - - + + + + + 0 + 25 + + + + + + - Com&pany: + Creation &date: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - lineEditCompany + lineEditDate + + + + + + + + 0 + 25 + + + + true + + + &Last modified by: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + lineEditLastMod + + + + + + + + 0 + 25 + + + + + Last &modification date: @@ -181,29 +198,6 @@ - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - true - - - - @@ -216,32 +210,61 @@ - - + + - Creation &date: + Com&pany: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - lineEditDate + lineEditCompany - - + + 0 25 - - true + + + + + + Commen&t: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + textEditComment + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 91 + 240 + + + + @@ -304,14 +327,6 @@ - lineEditName - lineEditCreator - lineEditDate - lineEditLastMod - lineEditLastModDate - lineEditCompany - buttonOk - buttonCancel diff --git a/src/Gui/DlgProjectInformationImp.cpp b/src/Gui/DlgProjectInformationImp.cpp index 5e4b115f0..c5f878693 100644 --- a/src/Gui/DlgProjectInformationImp.cpp +++ b/src/Gui/DlgProjectInformationImp.cpp @@ -46,6 +46,7 @@ DlgProjectInformationImp::DlgProjectInformationImp( App::Document* doc, QWidget* this->setupUi(this); lineEditName->setText(QString::fromUtf8(doc->Label.getValue())); lineEditPath->setText(QString::fromUtf8(doc->FileName.getValue())); + lineEditUuid->setText(QString::fromUtf8(doc->Uid.getValueStr().c_str())); lineEditCreator->setText(QString::fromUtf8(doc->CreatedBy.getValue())); lineEditDate->setText(QString::fromUtf8(doc->CreationDate.getValue())); lineEditLastMod->setText(QString::fromUtf8(doc->LastModifiedBy.getValue())); diff --git a/src/Gui/DlgSettingsViewColor.cpp b/src/Gui/DlgSettingsViewColor.cpp index 2bba4faac..bf8f3e216 100644 --- a/src/Gui/DlgSettingsViewColor.cpp +++ b/src/Gui/DlgSettingsViewColor.cpp @@ -71,6 +71,7 @@ void DlgSettingsViewColor::saveSettings() EditedVertexColor->onSave(); ConstructionColor->onSave(); FullyConstrainedColor->onSave(); + BoundingBoxColor->onSave(); DefaultShapeColor->onSave(); DefaultShapeLineColor->onSave(); DefaultShapeLineWidth->onSave(); @@ -94,6 +95,7 @@ void DlgSettingsViewColor::loadSettings() EditedVertexColor->onRestore(); ConstructionColor->onRestore(); FullyConstrainedColor->onRestore(); + BoundingBoxColor->onRestore(); DefaultShapeColor->onRestore(); DefaultShapeLineColor->onRestore(); DefaultShapeLineWidth->onRestore(); diff --git a/src/Gui/DlgSettingsViewColor.ui b/src/Gui/DlgSettingsViewColor.ui index 6acaaeaf9..8a7d92e6f 100644 --- a/src/Gui/DlgSettingsViewColor.ui +++ b/src/Gui/DlgSettingsViewColor.ui @@ -7,7 +7,7 @@ 0 0 601 - 565 + 598 @@ -520,6 +520,33 @@ + + + + Bounding box color + + + + + + + The color of bounding boxes in the 3D view + + + + 255 + 255 + 255 + + + + BoundingBoxColor + + + View + + + @@ -660,6 +687,7 @@ EditedVertexColor ConstructionColor FullyConstrainedColor + BoundingBoxColor radioButtonSimple SelectionColor_Background radioButtonGradient diff --git a/src/Gui/DlgToolbarsImp.cpp b/src/Gui/DlgToolbarsImp.cpp index 4b51f06f2..31827df5e 100644 --- a/src/Gui/DlgToolbarsImp.cpp +++ b/src/Gui/DlgToolbarsImp.cpp @@ -288,7 +288,11 @@ void DlgCustomToolbars::on_moveActionRightButton_clicked() QTreeWidgetItem* item = commandTreeWidget->currentItem(); if (item) { QTreeWidgetItem* current = toolbarTreeWidget->currentItem(); - if (current && !current->parent() && toolbarTreeWidget->isItemSelected(current)) { + if (!current) + current = toolbarTreeWidget->topLevelItem(0); + else if (current->parent()) + current = current->parent(); + if (current && !current->parent()) { QTreeWidgetItem* copy = new QTreeWidgetItem(current); copy->setText(0, item->text(1)); copy->setIcon(0, item->icon(0)); diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index f93aab1ee..84aac6696 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -571,43 +571,18 @@ bool Document::saveAs(void) getMainWindow()->showMessage(QObject::tr("Save document under new filename...")); 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()) { - FileDialog::setWorkingDirectory(fn); - QString file = fn.toLower(); - if (!file.endsWith(QLatin1String(".fcstd"))) { - fn += QLatin1String(".fcstd"); - QFileInfo fi; - fi.setFile(fn); - if (fi.exists()) { - // if we auto-append the extension make sure that we don't override an existing file - int ret = QMessageBox::question(getMainWindow(), QObject::tr("Save As"), - QObject::tr("%1 already exists.\n" - "Do you want to replace it?").arg(fn), - QMessageBox::Yes|QMessageBox::Default, - QMessageBox::No|QMessageBox::Escape); - if (ret != QMessageBox::Yes) - fn = QString(); - } - } - } - + QString fn = FileDialog::getSaveFileName(getMainWindow(), QObject::tr("Save %1 Document").arg(exe), + QString(), QObject::tr("%1 document (*.FCStd)").arg(exe)); if (!fn.isEmpty()) { QFileInfo fi; fi.setFile(fn); - QString bn = fi.baseName(); const char * DocName = App::GetApplication().getDocumentName(getDocument()); // save as new file name Gui::WaitCursor wc; - Command::doCommand(Command::Doc,"App.getDocument(\"%s\").FileName = \"%s\"" + Command::doCommand(Command::Doc,"App.getDocument(\"%s\").saveAs('%s')" , DocName, (const char*)fn.toUtf8()); - Command::doCommand(Command::Doc,"App.getDocument(\"%s\").Label = \"%s\"" - , DocName, (const char*)bn.toUtf8()); - Command::doCommand(Command::Doc,"App.getDocument(\"%s\").save()" - , DocName); setModified(false); getMainWindow()->appendRecentFile(fi.filePath()); @@ -695,9 +670,20 @@ void Document::RestoreDocFile(Base::Reader &reader) for (i=0 ;iRestore(xmlReader); + if (expanded) { + Gui::ViewProviderDocumentObject* vp = static_cast(pObj); + this->signalExpandObject(*vp, Gui::Expand); + } xmlReader.readEndElement("ViewProvider"); } xmlReader.readEndElement("ViewProviderData"); @@ -784,7 +770,9 @@ void Document::SaveDocFile (Base::Writer &writer) const const App::DocumentObject* doc = it->first; ViewProvider* obj = it->second; writer.Stream() << writer.ind() << "getNameInDocument() << "\">" << std::endl; + << doc->getNameInDocument() << "\" " + << "expanded=\"" << (doc->testStatus(App::Expand) ? 1:0) + << "\">" << std::endl; obj->Save(writer); writer.Stream() << writer.ind() << "" << std::endl; } diff --git a/src/Gui/DocumentModel.cpp b/src/Gui/DocumentModel.cpp index f65a58965..856200b99 100644 --- a/src/Gui/DocumentModel.cpp +++ b/src/Gui/DocumentModel.cpp @@ -30,6 +30,8 @@ # include #endif +#include + #include "DocumentModel.h" #include "Application.h" #include "BitmapFactory.h" @@ -54,8 +56,6 @@ namespace Gui { virtual ~DocumentModelIndex() { qDeleteAll(childItems); } - void reset() - { qDeleteAll(childItems); childItems.clear(); } void setParent(DocumentModelIndex* parent) { parentItem = parent; } DocumentModelIndex *parent() const @@ -64,7 +64,12 @@ namespace Gui { { childItems.append(child); child->setParent(this); } void removeChild(int row) { childItems.removeAt(row); } - + QList removeAll() + { + QList list = childItems; + childItems.clear(); + return list; + } DocumentModelIndex *child(int row) { return childItems.value(row); } int row() const @@ -91,11 +96,18 @@ namespace Gui { return Qt::ItemIsSelectable|Qt::ItemIsEnabled; } + protected: + void reset() + { qDeleteAll(childItems); childItems.clear(); } + protected: DocumentModelIndex() : parentItem(0) {} DocumentModelIndex *parentItem; QList childItems; }; + + // ------------------------------------------------------------------------ + // Root node class ApplicationIndex : public DocumentModelIndex { @@ -104,24 +116,22 @@ namespace Gui { public: ApplicationIndex(){} int findChild(const Gui::Document& d) const; - Qt::ItemFlags flags() const - { return Qt::ItemIsEnabled; } - QVariant data(int role) const - { - if (role == Qt::DecorationRole) { - return qApp->windowIcon(); - } - else if (role == Qt::DisplayRole) { - return DocumentModel::tr("Application"); - } - return QVariant(); - } + Qt::ItemFlags flags() const; + QVariant data(int role) const; }; + + // ------------------------------------------------------------------------ + // Document nodes class DocumentIndex : public DocumentModelIndex { + friend class ViewProviderIndex; TYPESYSTEM_HEADER(); static QIcon* documentIcon; + typedef boost::unordered_set IndexSet; + std::map vp_nodes; + void addToDocument(ViewProviderIndex*); + void removeFromDocument(ViewProviderIndex*); public: const Gui::Document& d; @@ -130,30 +140,18 @@ namespace Gui { if (!documentIcon) documentIcon = new QIcon(Gui::BitmapFactory().pixmap("Document")); } - int findViewProvider(const ViewProvider&) const; - void findViewProviders(const ViewProvider&, QList&) const; - QVariant data(int role) const + ~DocumentIndex() { - if (role == Qt::DecorationRole) { - return *documentIcon; - } - else if (role == Qt::DisplayRole) { - App::Document* doc = d.getDocument(); - return QString::fromUtf8(doc->Label.getValue()); - } - else if (role == Qt::FontRole) { - Document* doc = Application::Instance->activeDocument(); - QFont font; - font.setBold(doc==&d); - QVariant variant; - variant.setValue(font); - return variant; - } - - return QVariant(); + qDeleteAll(childItems); childItems.clear(); } + ViewProviderIndex* cloneViewProvider(const ViewProviderDocumentObject&) const; + int rowOfViewProvider(const ViewProviderDocumentObject&) const; + void findViewProviders(const ViewProviderDocumentObject&, QList&) const; + QVariant data(int role) const; }; + // ------------------------------------------------------------------------ + // Object nodes class ViewProviderIndex : public DocumentModelIndex { @@ -161,31 +159,18 @@ namespace Gui { public: const Gui::ViewProviderDocumentObject& v; - ViewProviderIndex(const Gui::ViewProviderDocumentObject& v) : v(v){} - void findViewProviders(const ViewProvider&, QList&) const; - QVariant data(int role) const - { - if (role == Qt::DecorationRole) { - return v.getIcon(); - } - else if (role == Qt::DisplayRole) { - App::DocumentObject* obj = v.getObject(); - return QString::fromUtf8(obj->Label.getValue()); - } - else if (role == Qt::FontRole) { - App::DocumentObject* obj = v.getObject(); - App::DocumentObject* act = obj->getDocument()->getActiveObject(); - QFont font; - font.setBold(obj==act); - QVariant variant; - variant.setValue(font); - return variant; - } + ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d); + ~ViewProviderIndex(); + ViewProviderIndex* clone() const; + void findViewProviders(const ViewProviderDocumentObject&, QList&) const; + QVariant data(int role) const; - return QVariant(); - } + private: + DocumentIndex* d; }; + // ------------------------------------------------------------------------ + int ApplicationIndex::findChild(const Gui::Document& d) const { int child=0; @@ -199,9 +184,50 @@ namespace Gui { return -1; } + Qt::ItemFlags ApplicationIndex::flags() const + { + return Qt::ItemIsEnabled; + } + + QVariant ApplicationIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return qApp->windowIcon(); + } + else if (role == Qt::DisplayRole) { + return DocumentModel::tr("Application"); + } + return QVariant(); + } + + // ------------------------------------------------------------------------ + QIcon* DocumentIndex::documentIcon = 0; - void DocumentIndex::findViewProviders(const ViewProvider& vp, + void DocumentIndex::addToDocument(ViewProviderIndex* vp) + { + vp_nodes[&vp->v].insert(vp); + } + + void DocumentIndex::removeFromDocument(ViewProviderIndex* vp) + { + vp_nodes[&vp->v].erase(vp); + } + + ViewProviderIndex* + DocumentIndex::cloneViewProvider(const ViewProviderDocumentObject& vp) const + { + std::map >::const_iterator it; + it = vp_nodes.find(&vp); + if (it != vp_nodes.end()) { + boost::unordered_set::const_iterator v; + v = it->second.begin(); + return (*v)->clone(); + } + return new ViewProviderIndex(vp, const_cast(this)); + } + + void DocumentIndex::findViewProviders(const ViewProviderDocumentObject& vp, QList& index) const { QList::const_iterator it; @@ -211,7 +237,7 @@ namespace Gui { } } - int DocumentIndex::findViewProvider(const ViewProvider& vp) const + int DocumentIndex::rowOfViewProvider(const ViewProviderDocumentObject& vp) const { QList::const_iterator it; int index=0; @@ -224,8 +250,52 @@ namespace Gui { return -1; } - void ViewProviderIndex::findViewProviders(const ViewProvider& vp, - QList& index) const + QVariant DocumentIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return *documentIcon; + } + else if (role == Qt::DisplayRole) { + App::Document* doc = d.getDocument(); + return QString::fromUtf8(doc->Label.getValue()); + } + else if (role == Qt::FontRole) { + Document* doc = Application::Instance->activeDocument(); + QFont font; + font.setBold(doc==&d); + QVariant variant; + variant.setValue(font); + return variant; + } + + return QVariant(); + } + + // ------------------------------------------------------------------------ + + ViewProviderIndex::ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d) + : v(v),d(d) + { + if (d) d->addToDocument(this); + } + + ViewProviderIndex::~ViewProviderIndex() + { + if (d) d->removeFromDocument(this); + } + + ViewProviderIndex* ViewProviderIndex::clone() const + { + ViewProviderIndex* copy = new ViewProviderIndex(this->v, this->d); + for (QList::const_iterator it = childItems.begin(); it != childItems.end(); ++it) { + ViewProviderIndex* c = static_cast(*it)->clone(); + copy->appendChild(c); + } + return copy; + } + + void ViewProviderIndex::findViewProviders(const ViewProviderDocumentObject& vp, + QList& index) const { if (&this->v == &vp) index.push_back(const_cast(this)); @@ -236,6 +306,30 @@ namespace Gui { } } + QVariant ViewProviderIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return v.getIcon(); + } + else if (role == Qt::DisplayRole) { + App::DocumentObject* obj = v.getObject(); + return QString::fromUtf8(obj->Label.getValue()); + } + else if (role == Qt::FontRole) { + App::DocumentObject* obj = v.getObject(); + App::DocumentObject* act = obj->getDocument()->getActiveObject(); + QFont font; + font.setBold(obj==act); + QVariant variant; + variant.setValue(font); + return variant; + } + + return QVariant(); + } + + // ------------------------------------------------------------------------ + TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentModelIndex, Base::BaseClass); TYPESYSTEM_SOURCE_ABSTRACT(Gui::ApplicationIndex,Gui::DocumentModelIndex); TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentIndex, Gui::DocumentModelIndex); @@ -350,7 +444,7 @@ void DocumentModel::slotNewObject(const Gui::ViewProviderDocumentObject& obj) QModelIndex parent = createIndex(index->row(),0,index); int count_obj = index->childCount(); beginInsertRows(parent, count_obj, count_obj); - index->appendChild(new ViewProviderIndex(obj)); + index->appendChild(new ViewProviderIndex(obj, index)); endInsertRows(); } } @@ -399,35 +493,47 @@ void DocumentModel::slotChangeObject(const Gui::ViewProviderDocumentObject& obj, else if (isPropertyLink(Prop)) { App::Document* doc = fea->getDocument(); Gui::Document* gdc = Application::Instance->getDocument(doc); - std::vector views = getLinkedObjects(*gdc, Prop); + std::vector views = claimChildren(*gdc, obj); int row = d->rootItem->findChild(*gdc); if (row > -1) { + QList del_items; DocumentIndex* doc_index = static_cast(d->rootItem->child(row)); - QList obj_index; - doc_index->findViewProviders(obj, obj_index); - - // remove from top level in document for (std::vector::iterator vp = views.begin(); vp != views.end(); ++vp) { - int row = doc_index->findViewProvider(**vp); + int row = doc_index->rowOfViewProvider(**vp); + // is it a top-level child in the document if (row >= 0) { DocumentModelIndex* child = doc_index->child(row); + del_items.push_back(child); QModelIndex parent = createIndex(doc_index->row(), 0, doc_index); beginRemoveRows(parent, row, row); doc_index->removeChild(row); - delete child; endRemoveRows(); } } + // get all occurrences of the view provider in the tree structure + QList obj_index; + doc_index->findViewProviders(obj, obj_index); for (QList::iterator it = obj_index.begin(); it != obj_index.end(); ++it) { QModelIndex parent = createIndex((*it)->row(),0,*it); int count_obj = (*it)->childCount(); - beginInsertRows(parent, count_obj, count_obj + (int)views.size()); - for (std::vector::iterator jt = views.begin(); jt != views.end(); ++jt) - (*it)->appendChild(new ViewProviderIndex(**jt)); + beginRemoveRows(parent, 0, count_obj); + // remove all children but do not yet delete them + QList items = (*it)->removeAll(); + endRemoveRows(); + + beginInsertRows(parent, 0, (int)views.size()); + for (std::vector::iterator vp = views.begin(); vp != views.end(); ++vp) { + ViewProviderIndex* clone = doc_index->cloneViewProvider(**vp); + (*it)->appendChild(clone); + } endInsertRows(); + + del_items.append(items); } + + qDeleteAll(del_items); } } } @@ -461,33 +567,27 @@ bool DocumentModel::isPropertyLink(const App::Property& prop) const { if (prop.isDerivedFrom(App::PropertyLink::getClassTypeId())) return true; + if (prop.isDerivedFrom(App::PropertyLinkSub::getClassTypeId())) + return true; if (prop.isDerivedFrom(App::PropertyLinkList::getClassTypeId())) return true; + if (prop.isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) + return true; return false; } std::vector -DocumentModel::getLinkedObjects(const Gui::Document& doc, const App::Property& prop) const +DocumentModel::claimChildren(const Gui::Document& doc, const ViewProviderDocumentObject& obj) const { - std::vector links; - if (prop.isDerivedFrom(App::PropertyLink::getClassTypeId())) { - App::DocumentObject* obj; - obj = static_cast(prop).getValue(); - ViewProvider* view = doc.getViewProvider(obj); + std::vector views; + std::vector childs = obj.claimChildren(); + for (std::vector::iterator it = childs.begin(); it != childs.end(); ++it) { + ViewProvider* view = doc.getViewProvider(*it); if (view && view->getTypeId().isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - links.push_back(static_cast(view)); - } - else if (prop.isDerivedFrom(App::PropertyLinkList::getClassTypeId())) { - const std::vector& refs = static_cast - (prop).getValues(); - for (std::vector::const_iterator it = refs.begin();it != refs.end(); ++it) { - ViewProvider* view = doc.getViewProvider(*it); - if (view && view->getTypeId().isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - links.push_back(static_cast(view)); - } + views.push_back(static_cast(view)); } - return links; + return views; } int DocumentModel::columnCount (const QModelIndex & /*parent*/) const diff --git a/src/Gui/DocumentModel.h b/src/Gui/DocumentModel.h index aaaa89ef0..fafe3587b 100644 --- a/src/Gui/DocumentModel.h +++ b/src/Gui/DocumentModel.h @@ -67,8 +67,8 @@ private: const Document* getDocument(const QModelIndex&) const; bool isPropertyLink(const App::Property&) const; - std::vector getLinkedObjects - (const Gui::Document&, const App::Property&) const; + std::vector claimChildren + (const Document&, const ViewProviderDocumentObject&) const; private: struct DocumentModelP *d; diff --git a/src/Gui/DownloadDialog.cpp b/src/Gui/DownloadDialog.cpp index 979e092ea..1c20d829d 100644 --- a/src/Gui/DownloadDialog.cpp +++ b/src/Gui/DownloadDialog.cpp @@ -27,7 +27,9 @@ # include #endif +#include #include "DownloadDialog.h" +#include "ui_DlgAuthorization.h" using namespace Gui::Dialog; @@ -202,6 +204,16 @@ void DownloadDialog::updateDataReadProgress(int bytesRead, int totalBytes) void DownloadDialog::slotAuthenticationRequired(const QString &hostName, quint16, QAuthenticator *authenticator) { + QDialog dlg; + Ui_DlgAuthorization ui; + ui.setupUi(&dlg); + dlg.adjustSize(); + ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm()).arg(hostName)); + + if (dlg.exec() == QDialog::Accepted) { + authenticator->setUser(ui.username->text()); + authenticator->setPassword(ui.password->text()); + } } diff --git a/src/Gui/DownloadItem.cpp b/src/Gui/DownloadItem.cpp new file mode 100644 index 000000000..46d226967 --- /dev/null +++ b/src/Gui/DownloadItem.cpp @@ -0,0 +1,583 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "DownloadItem.h" +#include "DownloadManager.h" +#include "Application.h" +#include "Document.h" +#include "MainWindow.h" +#include "FileDialog.h" +#include "ui_DlgAuthorization.h" + +using namespace Gui::Dialog; + + +EditTableView::EditTableView(QWidget *parent) + : QTableView(parent) +{ +} + +void EditTableView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Delete + || event->key() == Qt::Key_Backspace) + && model()) { + removeOne(); + } else { + QAbstractItemView::keyPressEvent(event); + } +} + +void EditTableView::removeOne() +{ + if (!model() || !selectionModel()) + return; + int row = currentIndex().row(); + model()->removeRow(row, rootIndex()); + QModelIndex idx = model()->index(row, 0, rootIndex()); + if (!idx.isValid()) + idx = model()->index(row - 1, 0, rootIndex()); + selectionModel()->select(idx, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +void EditTableView::removeAll() +{ + if (model()) + model()->removeRows(0, model()->rowCount(rootIndex()), rootIndex()); +} + +// ---------------------------------------------------------------------------- + +SqueezeLabel::SqueezeLabel(QWidget *parent) : QLabel(parent) +{ +} + +void SqueezeLabel::paintEvent(QPaintEvent *event) +{ + QFontMetrics fm = fontMetrics(); + if (fm.width(text()) > contentsRect().width()) { + QString elided = fm.elidedText(text(), Qt::ElideMiddle, width()); + QString oldText = text(); + setText(elided); + QLabel::paintEvent(event); + setText(oldText); + } else { + QLabel::paintEvent(event); + } +} + +// ---------------------------------------------------------------------------- + +#define AUTOSAVE_IN 1000 * 3 // seconds +#define MAXWAIT 1000 * 15 // seconds + +AutoSaver::AutoSaver(QObject *parent) : QObject(parent) +{ + Q_ASSERT(parent); +} + +AutoSaver::~AutoSaver() +{ + if (m_timer.isActive()) + qWarning() << "AutoSaver: still active when destroyed, changes not saved."; +} + +void AutoSaver::changeOccurred() +{ + if (m_firstChange.isNull()) + m_firstChange.start(); + + if (m_firstChange.elapsed() > MAXWAIT) { + saveIfNeccessary(); + } else { + m_timer.start(AUTOSAVE_IN, this); + } +} + +void AutoSaver::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_timer.timerId()) { + saveIfNeccessary(); + } else { + QObject::timerEvent(event); + } +} + +void AutoSaver::saveIfNeccessary() +{ + if (!m_timer.isActive()) + return; + m_timer.stop(); + m_firstChange = QTime(); + if (!QMetaObject::invokeMethod(parent(), "save", Qt::DirectConnection)) { + qWarning() << "AutoSaver: error invoking slot save() on parent"; + } +} + +// ---------------------------------------------------------------------------- + +NetworkAccessManager::NetworkAccessManager(QObject *parent) + : QNetworkAccessManager(parent) +{ + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), + SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*))); + connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + SLOT(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))); + + QNetworkDiskCache *diskCache = new QNetworkDiskCache(this); + QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation); + diskCache->setCacheDirectory(location); + setCache(diskCache); +} + +void NetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *auth) +{ + QWidget *mainWindow = Gui::getMainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui_DlgAuthorization passwordDialog; + passwordDialog.setupUi(&dialog); + dialog.adjustSize(); + + QString introMessage = tr("Enter username and password for \"%1\" at %2"); + introMessage = introMessage.arg(Qt::escape(reply->url().toString())).arg(Qt::escape(reply->url().toString())); + passwordDialog.siteDescription->setText(introMessage); + passwordDialog.siteDescription->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(passwordDialog.username->text()); + auth->setPassword(passwordDialog.password->text()); + } +} + +void NetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth) +{ + QWidget *mainWindow = Gui::getMainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui_DlgAuthorization proxyDialog; + proxyDialog.setupUi(&dialog); + dialog.adjustSize(); + + QString introMessage = tr("Connect to proxy \"%1\" using:"); + introMessage = introMessage.arg(Qt::escape(proxy.hostName())); + proxyDialog.siteDescription->setText(introMessage); + proxyDialog.siteDescription->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(proxyDialog.username->text()); + auth->setPassword(proxyDialog.password->text()); + } +} + +// ---------------------------------------------------------------------------- + +DownloadItem::DownloadItem(QNetworkReply *reply, bool requestFileName, QWidget *parent) + : QWidget(parent) + , m_reply(reply) + , m_requestFileName(requestFileName) + , m_bytesReceived(0) +{ + setupUi(this); + QPalette p = downloadInfoLabel->palette(); + p.setColor(QPalette::Text, Qt::darkGray); + downloadInfoLabel->setPalette(p); + progressBar->setMaximum(0); + tryAgainButton->hide(); + connect(stopButton, SIGNAL(clicked()), this, SLOT(stop())); + connect(openButton, SIGNAL(clicked()), this, SLOT(open())); + connect(tryAgainButton, SIGNAL(clicked()), this, SLOT(tryAgain())); + + init(); +} + +void DownloadItem::init() +{ + if (!m_reply) + return; + + // attach to the m_reply + m_url = m_reply->url(); + m_reply->setParent(this); + connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), + this, SLOT(downloadProgress(qint64, qint64))); + connect(m_reply, SIGNAL(metaDataChanged()), + this, SLOT(metaDataChanged())); + connect(m_reply, SIGNAL(finished()), + this, SLOT(finished())); + + // reset info + downloadInfoLabel->clear(); + progressBar->setValue(0); + getFileName(); + + // start timer for the download estimation + m_downloadTime.start(); + + if (m_reply->error() != QNetworkReply::NoError) { + error(m_reply->error()); + finished(); + } +} + +QString DownloadItem::getDownloadDirectory() const +{ + QString exe = QString::fromAscii(App::GetApplication().getExecutableName()); + QString path = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); + QString dirPath = QDir(path).filePath(exe); + Base::Reference hPath = App::GetApplication().GetUserParameter().GetGroup("BaseApp") + ->GetGroup("Preferences")->GetGroup("General"); + std::string dir = hPath->GetASCII("DownloadPath", ""); + if (!dir.empty()) { + dirPath = QString::fromUtf8(dir.c_str()); + } + + if (QFileInfo(dirPath).exists() || QDir().mkpath(dirPath)) { + return dirPath; + } + else { + return path; + } +} + +void DownloadItem::getFileName() +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + //QString defaultLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); + QString defaultLocation = getDownloadDirectory(); + QString downloadDirectory = settings.value(QLatin1String("downloadDirectory"), defaultLocation).toString(); + if (!downloadDirectory.isEmpty()) + downloadDirectory += QLatin1Char('/'); + + QString defaultFileName = saveFileName(downloadDirectory); + QString fileName = defaultFileName; + if (m_requestFileName) { + fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName); + if (fileName.isEmpty()) { + m_reply->close(); + fileNameLabel->setText(tr("Download canceled: %1").arg(QFileInfo(defaultFileName).fileName())); + return; + } + } + m_output.setFileName(fileName); + fileNameLabel->setText(QFileInfo(m_output.fileName()).fileName()); + fileNameLabel->setToolTip(m_output.fileName()); + if (m_requestFileName) + downloadReadyRead(); +} + +QString DownloadItem::saveFileName(const QString &directory) const +{ + // Move this function into QNetworkReply to also get file name sent from the server + QString path = m_url.path(); + if (!m_fileName.isEmpty()) + path = m_fileName; + QFileInfo info(path); + QString baseName = info.completeBaseName(); + QString endName = info.suffix(); + + if (baseName.isEmpty()) { + baseName = QLatin1String("unnamed_download"); + qDebug() << "DownloadManager:: downloading unknown file:" << m_url; + } + QString name = directory + baseName + QLatin1Char('.') + endName; + if (QFile::exists(name)) { + // already exists, don't overwrite + int i = 1; + do { + name = directory + baseName + QLatin1Char('-') + QString::number(i++) + QLatin1Char('.') + endName; + } while (QFile::exists(name)); + } + return name; +} + + +void DownloadItem::stop() +{ + setUpdatesEnabled(false); + stopButton->setEnabled(false); + stopButton->hide(); + tryAgainButton->setEnabled(true); + tryAgainButton->show(); + setUpdatesEnabled(true); + m_reply->abort(); +} + +void DownloadItem::open() +{ + QFileInfo info(m_output); + QString selectedFilter; + QStringList fileList; + fileList << info.absoluteFilePath(); + SelectModule::Dict dict = SelectModule::importHandler(fileList, selectedFilter); + + // load the files with the associated modules + if (!dict.isEmpty()) { + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + if (doc) { + for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) { + Gui::Application::Instance->importFrom(it.key().toUtf8(), + doc->getDocument()->getName(), it.value().toAscii()); + } + } + else { + for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) { + Gui::Application::Instance->open(it.key().toUtf8(), it.value().toAscii()); + } + } + } + else { + QUrl url = QUrl::fromLocalFile(info.absolutePath()); + QDesktopServices::openUrl(url); + } +} + +void DownloadItem::openFolder() +{ + QFileInfo info(m_output); + QUrl url = QUrl::fromLocalFile(info.absolutePath()); + QDesktopServices::openUrl(url); +} + +void DownloadItem::tryAgain() +{ + if (!tryAgainButton->isEnabled()) + return; + + tryAgainButton->setEnabled(false); + tryAgainButton->setVisible(false); + stopButton->setEnabled(true); + stopButton->setVisible(true); + progressBar->setVisible(true); + + QNetworkReply *r = DownloadManager::getInstance()->networkAccessManager()->get(QNetworkRequest(m_url)); + if (m_reply) + m_reply->deleteLater(); + if (m_output.exists()) + m_output.remove(); + m_reply = r; + init(); + /*emit*/ statusChanged(); +} + +void DownloadItem::contextMenuEvent (QContextMenuEvent * e) +{ + QMenu menu; + QAction* a = menu.addAction(tr("Open containing folder"), this, SLOT(openFolder())); + a->setEnabled(m_output.exists()); + menu.exec(e->globalPos()); +} + +void DownloadItem::downloadReadyRead() +{ + if (m_requestFileName && m_output.fileName().isEmpty()) + return; + if (!m_output.isOpen()) { + // in case someone else has already put a file there + if (!m_requestFileName) + getFileName(); + if (!m_output.open(QIODevice::WriteOnly)) { + downloadInfoLabel->setText(tr("Error opening save file: %1") + .arg(m_output.errorString())); + stopButton->click(); + /*emit*/ statusChanged(); + return; + } + downloadInfoLabel->setToolTip(m_url.toString()); + /*emit*/ statusChanged(); + } + if (-1 == m_output.write(m_reply->readAll())) { + downloadInfoLabel->setText(tr("Error saving: %1") + .arg(m_output.errorString())); + stopButton->click(); + } +} + +void DownloadItem::error(QNetworkReply::NetworkError) +{ + qDebug() << "DownloadItem::error" << m_reply->errorString() << m_url; + downloadInfoLabel->setText(tr("Network Error: %1").arg(m_reply->errorString())); + tryAgainButton->setEnabled(true); + tryAgainButton->setVisible(true); +} + +void DownloadItem::metaDataChanged() +{ + if (m_reply->hasRawHeader(QByteArray("Content-Disposition"))) { + QByteArray header = m_reply->rawHeader(QByteArray("Content-Disposition")); + int index = header.indexOf("filename="); + if (index >= 0) { + header = header.mid(index+9); + if (header.startsWith("\"") || header.startsWith("'")) + header = header.mid(1); + if (header.endsWith("\"") || header.endsWith("'")) + header.chop(1); + m_fileName = QUrl::fromPercentEncoding(header); + } + else { + index = header.indexOf("filename*=UTF-8''"); + if (index >= 0) { + header = header.mid(index+17); + if (header.startsWith("\"") || header.startsWith("'")) + header = header.mid(1); + if (header.endsWith("\"") || header.endsWith("'")) + header.chop(1); + m_fileName = QUrl::fromPercentEncoding(header); + } + } + } + + QVariant statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + if (!statusCode.isValid()) + return; + int status = statusCode.toInt(); + if (status != 200) { + QString reason = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + qDebug() << reason; + } +} + +void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + m_bytesReceived = bytesReceived; + if (bytesTotal == -1) { + progressBar->setValue(0); + progressBar->setMaximum(0); + } else { + progressBar->setValue(bytesReceived); + progressBar->setMaximum(bytesTotal); + } + updateInfoLabel(); +} + +void DownloadItem::updateInfoLabel() +{ + //if (m_reply->error() == QNetworkReply::NoError) + // return; + + qint64 bytesTotal = progressBar->maximum(); + bool running = !downloadedSuccessfully(); + + // update info label + double speed = m_bytesReceived * 1000.0 / m_downloadTime.elapsed(); + double timeRemaining = ((double)(bytesTotal - m_bytesReceived)) / speed; + QString timeRemainingString = tr("seconds"); + if (timeRemaining > 60) { + timeRemaining = timeRemaining / 60; + timeRemainingString = tr("minutes"); + } + timeRemaining = floor(timeRemaining); + + // When downloading the eta should never be 0 + if (timeRemaining == 0) + timeRemaining = 1; + + QString info; + if (running) { + QString remaining; + if (bytesTotal != 0) + remaining = tr("- %4 %5 remaining") + .arg(timeRemaining) + .arg(timeRemainingString); + info = QString(tr("%1 of %2 (%3/sec) %4")) + .arg(dataString(m_bytesReceived)) + .arg(bytesTotal == 0 ? tr("?") : dataString(bytesTotal)) + .arg(dataString((int)speed)) + .arg(remaining); + } else { + if (m_bytesReceived == bytesTotal) + info = dataString(m_output.size()); + else + info = tr("%1 of %2 - Stopped") + .arg(dataString(m_bytesReceived)) + .arg(dataString(bytesTotal)); + } + downloadInfoLabel->setText(info); +} + +QString DownloadItem::dataString(int size) const +{ + QString unit; + if (size < 1024) { + unit = tr("bytes"); + } else if (size < 1024*1024) { + size /= 1024; + unit = tr("kB"); + } else { + size /= 1024*1024; + unit = tr("MB"); + } + return QString(QLatin1String("%1 %2")).arg(size).arg(unit); +} + +bool DownloadItem::downloading() const +{ + return (progressBar->isVisible()); +} + +bool DownloadItem::downloadedSuccessfully() const +{ + return (stopButton->isHidden() && tryAgainButton->isHidden()); +} + +void DownloadItem::finished() +{ + progressBar->hide(); + stopButton->setEnabled(false); + stopButton->hide(); + m_output.close(); + updateInfoLabel(); + /*emit*/ statusChanged(); +} + +#include "moc_DownloadItem.cpp" diff --git a/src/Gui/DownloadItem.h b/src/Gui/DownloadItem.h new file mode 100644 index 000000000..2503f2ca0 --- /dev/null +++ b/src/Gui/DownloadItem.h @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_DIALOG_DOWNLOADITEM_H +#define GUI_DIALOG_DOWNLOADITEM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class AutoSaver; +class QFileIconProvider; + +class EditTableView : public QTableView +{ + Q_OBJECT + +public: + EditTableView(QWidget *parent = 0); + void keyPressEvent(QKeyEvent *event); + +public Q_SLOTS: + void removeOne(); + void removeAll(); +}; + +class SqueezeLabel : public QLabel +{ + Q_OBJECT + +public: + SqueezeLabel(QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent *event); + +}; + +/* + This class will call the save() slot on the parent object when the parent changes. + It will wait several seconds after changed() to combining multiple changes and + prevent continuous writing to disk. + */ +class AutoSaver : public QObject { + +Q_OBJECT + +public: + AutoSaver(QObject *parent); + ~AutoSaver(); + void saveIfNeccessary(); + +public Q_SLOTS: + void changeOccurred(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + QBasicTimer m_timer; + QTime m_firstChange; + +}; + +class NetworkAccessManager : public QNetworkAccessManager +{ + Q_OBJECT + +public: + NetworkAccessManager(QObject *parent = 0); + +private Q_SLOTS: + void authenticationRequired(QNetworkReply *reply, QAuthenticator *auth); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); +}; + +#include "ui_DownloadItem.h" + +namespace Gui { +namespace Dialog { +class DownloadModel; + +class DownloadItem : public QWidget, public Ui_DownloadItem +{ + Q_OBJECT + +Q_SIGNALS: + void statusChanged(); + +public: + DownloadItem(QNetworkReply *reply = 0, bool requestFileName = false, QWidget *parent = 0); + bool downloading() const; + bool downloadedSuccessfully() const; + + QUrl m_url; + QString m_fileName; + + QFile m_output; + QNetworkReply *m_reply; + +private Q_SLOTS: + void stop(); + void tryAgain(); + void open(); + void openFolder(); + + void downloadReadyRead(); + void error(QNetworkReply::NetworkError code); + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void metaDataChanged(); + void finished(); + +private: + void contextMenuEvent(QContextMenuEvent *); + void getFileName(); + void init(); + void updateInfoLabel(); + QString dataString(int size) const; + QString getDownloadDirectory() const; + QString saveFileName(const QString &directory) const; + + bool m_requestFileName; + qint64 m_bytesReceived; + QTime m_downloadTime; +}; + +} // namespace Dialog +} // namespace Gui + +#endif // GUI_DIALOG_DOWNLOADITEM_H diff --git a/src/Gui/DownloadItem.ui b/src/Gui/DownloadItem.ui new file mode 100644 index 000000000..7a6cbf774 --- /dev/null +++ b/src/Gui/DownloadItem.ui @@ -0,0 +1,140 @@ + + + DownloadItem + + + + 0 + 0 + 423 + 98 + + + + Form + + + + 0 + + + + + + 0 + 0 + + + + Ico + + + + + + + + + + 0 + 0 + + + + Filename + + + + + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + Qt::Vertical + + + + 17 + 1 + + + + + + + + false + + + + :/icons/view-refresh.svg:/icons/view-refresh.svg + + + + + + + + :/icons/process-stop.svg:/icons/process-stop.svg + + + + + + + + :/icons/document-open.svg:/icons/document-open.svg + + + + + + + Qt::Vertical + + + + 17 + 5 + + + + + + + + + + + SqueezeLabel + QWidget +
DownloadItem.h
+
+
+ + + + +
diff --git a/src/Gui/DownloadManager.cpp b/src/Gui/DownloadManager.cpp new file mode 100644 index 000000000..8846863ec --- /dev/null +++ b/src/Gui/DownloadManager.cpp @@ -0,0 +1,303 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DownloadItem.h" +#include "DownloadManager.h" +#include "ui_DownloadManager.h" +#include "DockWindowManager.h" +#include "MainWindow.h" + +using namespace Gui::Dialog; + +/* TRANSLATOR Gui::Dialog::DownloadManager */ + +DownloadManager* DownloadManager::self = 0; + +DownloadManager* DownloadManager::getInstance() +{ + if (!self) + self = new DownloadManager(Gui::getMainWindow()); + return self; +} + +DownloadManager::DownloadManager(QWidget *parent) + : QDialog(parent) + , m_autoSaver(new AutoSaver(this)) + , m_manager(new NetworkAccessManager(this)) + , m_iconProvider(0) + , m_removePolicy(Never) + , ui(new Ui_DownloadManager()) +{ + ui->setupUi(this); + ui->downloadsView->setShowGrid(false); + ui->downloadsView->verticalHeader()->hide(); + ui->downloadsView->horizontalHeader()->hide(); + ui->downloadsView->setAlternatingRowColors(true); + ui->downloadsView->horizontalHeader()->setStretchLastSection(true); + m_model = new DownloadModel(this); + ui->downloadsView->setModel(m_model); + connect(ui->cleanupButton, SIGNAL(clicked()), this, SLOT(cleanup())); + load(); + + Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); + QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Download Manager"), + this, Qt::BottomDockWidgetArea); + dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); + dw->show(); +} + +DownloadManager::~DownloadManager() +{ + m_autoSaver->changeOccurred(); + m_autoSaver->saveIfNeccessary(); + if (m_iconProvider) + delete m_iconProvider; + delete ui; +} + +int DownloadManager::activeDownloads() const +{ + int count = 0; + for (int i = 0; i < m_downloads.count(); ++i) { + if (m_downloads.at(i)->stopButton->isEnabled()) + ++count; + } + return count; +} + +void DownloadManager::download(const QNetworkRequest &request, bool requestFileName) +{ + if (request.url().isEmpty()) + return; + handleUnsupportedContent(m_manager->get(request), requestFileName); +} + +void DownloadManager::handleUnsupportedContent(QNetworkReply *reply, bool requestFileName) +{ + if (!reply || reply->url().isEmpty()) + return; + QVariant header = reply->header(QNetworkRequest::ContentLengthHeader); + bool ok; + int size = header.toInt(&ok); + if (ok && size == 0) + return; + + DownloadItem *item = new DownloadItem(reply, requestFileName, this); + addItem(item); +} + +void DownloadManager::addItem(DownloadItem *item) +{ + connect(item, SIGNAL(statusChanged()), this, SLOT(updateRow())); + int row = m_downloads.count(); + m_model->beginInsertRows(QModelIndex(), row, row); + m_downloads.append(item); + m_model->endInsertRows(); + updateItemCount(); + show(); + ui->downloadsView->setIndexWidget(m_model->index(row, 0), item); + QIcon icon = style()->standardIcon(QStyle::SP_FileIcon); + item->fileIcon->setPixmap(icon.pixmap(48, 48)); + ui->downloadsView->setRowHeight(row, item->sizeHint().height()); +} + +void DownloadManager::updateRow() +{ + DownloadItem *item = qobject_cast(sender()); + int row = m_downloads.indexOf(item); + if (-1 == row) + return; + if (!m_iconProvider) + m_iconProvider = new QFileIconProvider(); + QIcon icon = m_iconProvider->icon(item->m_output.fileName()); + if (icon.isNull()) + icon = style()->standardIcon(QStyle::SP_FileIcon); + item->fileIcon->setPixmap(icon.pixmap(48, 48)); + ui->downloadsView->setRowHeight(row, item->minimumSizeHint().height()); + + bool remove = false; + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (!item->downloading() + && globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + remove = true; + + if (item->downloadedSuccessfully() + && removePolicy() == DownloadManager::SuccessFullDownload) { + remove = true; + } + if (remove) + m_model->removeRow(row); + + ui->cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0); +} + +DownloadManager::RemovePolicy DownloadManager::removePolicy() const +{ + return m_removePolicy; +} + +void DownloadManager::setRemovePolicy(RemovePolicy policy) +{ + if (policy == m_removePolicy) + return; + m_removePolicy = policy; + m_autoSaver->changeOccurred(); +} + +void DownloadManager::save() const +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy")); + settings.setValue(QLatin1String("removeDownloadsPolicy"), QLatin1String(removePolicyEnum.valueToKey(m_removePolicy))); + settings.setValue(QLatin1String("size"), size()); + if (m_removePolicy == Exit) + return; + + for (int i = 0; i < m_downloads.count(); ++i) { + QString key = QString(QLatin1String("download_%1_")).arg(i); + settings.setValue(key + QLatin1String("url"), m_downloads[i]->m_url); + settings.setValue(key + QLatin1String("location"), QFileInfo(m_downloads[i]->m_output).filePath()); + settings.setValue(key + QLatin1String("done"), m_downloads[i]->downloadedSuccessfully()); + } + int i = m_downloads.count(); + QString key = QString(QLatin1String("download_%1_")).arg(i); + while (settings.contains(key + QLatin1String("url"))) { + settings.remove(key + QLatin1String("url")); + settings.remove(key + QLatin1String("location")); + settings.remove(key + QLatin1String("done")); + key = QString(QLatin1String("download_%1_")).arg(++i); + } +} + +void DownloadManager::load() +{ + QSettings settings; + settings.beginGroup(QLatin1String("downloadmanager")); + QSize size = settings.value(QLatin1String("size")).toSize(); + if (size.isValid()) + resize(size); + QByteArray value = settings.value(QLatin1String("removeDownloadsPolicy"), QLatin1String("Never")).toByteArray(); + QMetaEnum removePolicyEnum = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("RemovePolicy")); + m_removePolicy = removePolicyEnum.keyToValue(value) == -1 ? + Never : + static_cast(removePolicyEnum.keyToValue(value)); + + int i = 0; + QString key = QString(QLatin1String("download_%1_")).arg(i); + while (settings.contains(key + QLatin1String("url"))) { + QUrl url = settings.value(key + QLatin1String("url")).toUrl(); + QString fileName = settings.value(key + QLatin1String("location")).toString(); + bool done = settings.value(key + QLatin1String("done"), true).toBool(); + if (!url.isEmpty() && !fileName.isEmpty()) { + DownloadItem *item = new DownloadItem(0, this); + item->m_output.setFileName(fileName); + item->fileNameLabel->setText(QFileInfo(item->m_output.fileName()).fileName()); + item->m_url = url; + item->stopButton->setVisible(false); + item->stopButton->setEnabled(false); + item->tryAgainButton->setVisible(!done); + item->tryAgainButton->setEnabled(!done); + item->progressBar->setVisible(!done); + addItem(item); + } + key = QString(QLatin1String("download_%1_")).arg(++i); + } + ui->cleanupButton->setEnabled(m_downloads.count() - activeDownloads() > 0); +} + +void DownloadManager::cleanup() +{ + if (m_downloads.isEmpty()) + return; + m_model->removeRows(0, m_downloads.count()); + updateItemCount(); + if (m_downloads.isEmpty() && m_iconProvider) { + delete m_iconProvider; + m_iconProvider = 0; + } + m_autoSaver->changeOccurred(); +} + +void DownloadManager::updateItemCount() +{ + int count = m_downloads.count(); + ui->itemCount->setText(count == 1 ? tr("1 Download") : tr("%1 Downloads").arg(count)); +} + +// ---------------------------------------------------------------------------- + +DownloadModel::DownloadModel(DownloadManager *downloadManager, QObject *parent) + : QAbstractListModel(parent) + , m_downloadManager(downloadManager) +{ +} + +QVariant DownloadModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= rowCount(index.parent())) + return QVariant(); + if (role == Qt::ToolTipRole) + if (!m_downloadManager->m_downloads.at(index.row())->downloadedSuccessfully()) + return m_downloadManager->m_downloads.at(index.row())->downloadInfoLabel->text(); + return QVariant(); +} + +int DownloadModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : m_downloadManager->m_downloads.count(); +} + +bool DownloadModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + int lastRow = row + count - 1; + for (int i = lastRow; i >= row; --i) { + if (m_downloadManager->m_downloads.at(i)->downloadedSuccessfully() + || m_downloadManager->m_downloads.at(i)->tryAgainButton->isEnabled()) { + beginRemoveRows(parent, i, i); + m_downloadManager->m_downloads.takeAt(i)->deleteLater(); + endRemoveRows(); + } + } + m_downloadManager->m_autoSaver->changeOccurred(); + return true; +} + +#include "moc_DownloadManager.cpp" diff --git a/src/Gui/DownloadManager.h b/src/Gui/DownloadManager.h new file mode 100644 index 000000000..48d847eef --- /dev/null +++ b/src/Gui/DownloadManager.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (c) 2013 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef GUI_DIALOG_DOWNLOADMANAGER_H +#define GUI_DIALOG_DOWNLOADMANAGER_H + +#include +#include +#include +#include + +class AutoSaver; +class QFileIconProvider; + +namespace Gui { +namespace Dialog { +class DownloadItem; +class DownloadModel; +class Ui_DownloadManager; + +class GuiExport DownloadManager : public QDialog +{ + Q_OBJECT + Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy) + Q_ENUMS(RemovePolicy) + +public: + enum RemovePolicy { + Never, + Exit, + SuccessFullDownload + }; + + static DownloadManager* getInstance(); + +private: + DownloadManager(QWidget *parent = 0); + ~DownloadManager(); + +public: + int activeDownloads() const; + QNetworkAccessManager * networkAccessManager() + { return m_manager; } + + RemovePolicy removePolicy() const; + void setRemovePolicy(RemovePolicy policy); + +public Q_SLOTS: + void download(const QNetworkRequest &request, bool requestFileName = false); + inline void download(const QUrl &url, bool requestFileName = false) + { download(QNetworkRequest(url), requestFileName); } + void handleUnsupportedContent(QNetworkReply *reply, bool requestFileName = false); + void cleanup(); + +private Q_SLOTS: + void save() const; + void updateRow(); + +private: + void addItem(DownloadItem *item); + void updateItemCount(); + void load(); + + AutoSaver *m_autoSaver; + DownloadModel *m_model; + QNetworkAccessManager *m_manager; + QFileIconProvider *m_iconProvider; + QList m_downloads; + RemovePolicy m_removePolicy; + friend class DownloadModel; + +private: + Ui_DownloadManager* ui; + static DownloadManager* self; +}; + +class DownloadModel : public QAbstractListModel +{ + friend class DownloadManager; + Q_OBJECT + +public: + DownloadModel(DownloadManager *downloadManager, QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + DownloadManager *m_downloadManager; + +}; + +} // namespace Dialog +} // namespace Gui + +#endif // GUI_DIALOG_DOWNLOADMANAGER_H diff --git a/src/Gui/DownloadManager.ui b/src/Gui/DownloadManager.ui new file mode 100644 index 000000000..a040b0585 --- /dev/null +++ b/src/Gui/DownloadManager.ui @@ -0,0 +1,83 @@ + + Gui::Dialog::DownloadManager + + + + 0 + 0 + 332 + 252 + + + + Downloads + + + + 0 + + + 0 + + + + + + + + + + false + + + Clean up + + + + + + + Qt::Horizontal + + + + 58 + 24 + + + + + + + + + + 0 Items + + + + + + + Qt::Horizontal + + + + 148 + 20 + + + + + + + + + EditTableView + QTableView +
DownloadItem.h
+
+
+ + +
diff --git a/src/Gui/FileDialog.cpp b/src/Gui/FileDialog.cpp index a34c62fea..46b044aa6 100644 --- a/src/Gui/FileDialog.cpp +++ b/src/Gui/FileDialog.cpp @@ -26,11 +26,14 @@ # include # include # include +# include # include # include # include # include # include +# include +# include #endif #include @@ -45,6 +48,32 @@ using namespace Gui; /* TRANSLATOR Gui::FileDialog */ +FileDialog::FileDialog(QWidget * parent) + : QFileDialog(parent) +{ + connect(this, SIGNAL(filterSelected(const QString&)), + this, SLOT(onSelectedFilter(const QString&))); +} + +FileDialog::~FileDialog() +{ +} + +void FileDialog::onSelectedFilter(const QString& filter) +{ + QRegExp rx(QLatin1String("\\(\\*.(\\w+)")); + QString suf = selectedFilter(); + if (rx.indexIn(suf) >= 0) { + suf = rx.cap(1); + setDefaultSuffix(suf.toLower()); + } +} + +void FileDialog::accept() +{ + QFileDialog::accept(); +} + /** * This is a convenience static function that will return a file name selected by the user. The file does not have to exist. */ @@ -84,7 +113,37 @@ QString FileDialog::getSaveFileName (QWidget * parent, const QString & caption, // NOTE: We must not change the specified file name afterwards as we may return the name of an already // existing file. Hence we must extract the first matching suffix from the filter list and append it // before showing the file dialog. +#if defined(FC_OS_LINUX) + QList urls; + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)); + urls << QUrl::fromLocalFile(getWorkingDirectory()); + + QString file; + FileDialog dlg(parent); + dlg.setWindowTitle(windowTitle); + dlg.setSidebarUrls(urls); + dlg.setIconProvider(new FileIconProvider()); + dlg.setFileMode(QFileDialog::AnyFile); + dlg.setAcceptMode(QFileDialog::AcceptSave); + dlg.setDirectory(dirName); + dlg.setOptions(options); + dlg.setFilters(filter.split(QLatin1String(";;"))); + dlg.onSelectedFilter(dlg.selectedFilter()); + dlg.setNameFilterDetailsVisible(true); + dlg.setConfirmOverwrite(true); + if (dlg.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dlg.selectedFilter(); + file = dlg.selectedFiles().front(); + } +#else QString file = QFileDialog::getSaveFileName(parent, windowTitle, dirName, filter, selectedFilter, options); +#endif if (!file.isEmpty()) { setWorkingDirectory(file); return file; @@ -129,7 +188,36 @@ QString FileDialog::getOpenFileName(QWidget * parent, const QString & caption, c #if defined(FC_OS_MACOSX) options |= QFileDialog::DontUseNativeDialog; #endif + +#if defined(FC_OS_LINUX) + QList urls; + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)); + urls << QUrl::fromLocalFile(getWorkingDirectory()); + + QString file; + FileDialog dlg(parent); + dlg.setWindowTitle(windowTitle); + dlg.setSidebarUrls(urls); + dlg.setIconProvider(new FileIconProvider()); + dlg.setFileMode(QFileDialog::ExistingFile); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setDirectory(dirName); + dlg.setOptions(options); + dlg.setFilters(filter.split(QLatin1String(";;"))); + dlg.setNameFilterDetailsVisible(true); + if (dlg.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dlg.selectedFilter(); + file = dlg.selectedFiles().front(); + } +#else QString file = QFileDialog::getOpenFileName(parent, windowTitle, dirName, filter, selectedFilter, options); +#endif if (!file.isEmpty()) { setWorkingDirectory(file); return file; @@ -155,7 +243,36 @@ QStringList FileDialog::getOpenFileNames (QWidget * parent, const QString & capt #if defined(FC_OS_MACOSX) options |= QFileDialog::DontUseNativeDialog; #endif + +#if defined(FC_OS_LINUX) + QList urls; + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DesktopLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); + urls << QUrl::fromLocalFile(QDesktopServices::storageLocation(QDesktopServices::MoviesLocation)); + urls << QUrl::fromLocalFile(getWorkingDirectory()); + + QStringList files; + FileDialog dlg(parent); + dlg.setWindowTitle(windowTitle); + dlg.setSidebarUrls(urls); + dlg.setIconProvider(new FileIconProvider()); + dlg.setFileMode(QFileDialog::ExistingFiles); + dlg.setAcceptMode(QFileDialog::AcceptOpen); + dlg.setDirectory(dirName); + dlg.setOptions(options); + dlg.setFilters(filter.split(QLatin1String(";;"))); + dlg.setNameFilterDetailsVisible(true); + if (dlg.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dlg.selectedFilter(); + files = dlg.selectedFiles(); + } +#else QStringList files = QFileDialog::getOpenFileNames(parent, windowTitle, dirName, filter, selectedFilter, options); +#endif if (!files.isEmpty()) { setWorkingDirectory(files.front()); } @@ -322,17 +439,17 @@ FileIconProvider::~FileIconProvider() { } -QIcon FileIconProvider::icon ( IconType type ) const +QIcon FileIconProvider::icon(IconType type) const { return QFileIconProvider::icon(type); } -QIcon FileIconProvider::icon ( const QFileInfo & info ) const +QIcon FileIconProvider::icon(const QFileInfo & info) const { return QFileIconProvider::icon(info); } -QString FileIconProvider::type ( const QFileInfo & info ) const +QString FileIconProvider::type(const QFileInfo & info) const { return QFileIconProvider::type(info); } diff --git a/src/Gui/FileDialog.h b/src/Gui/FileDialog.h index 71359dd55..ddca6a647 100644 --- a/src/Gui/FileDialog.h +++ b/src/Gui/FileDialog.h @@ -56,6 +56,14 @@ public: static QString getWorkingDirectory(); static void setWorkingDirectory( const QString& ); + + FileDialog(QWidget * parent = 0); + ~FileDialog(); + + void accept(); + +private Q_SLOTS: + void onSelectedFilter(const QString&); }; // ---------------------------------------------------------------------- @@ -105,9 +113,9 @@ public: FileIconProvider(); ~FileIconProvider(); - QIcon icon ( IconType type ) const; - QIcon icon ( const QFileInfo & info ) const; - QString type ( const QFileInfo & info ) const; + QIcon icon(IconType type) const; + QIcon icon(const QFileInfo & info) const; + QString type(const QFileInfo & info) const; }; // ---------------------------------------------------------------------- diff --git a/src/Gui/Flag.cpp b/src/Gui/Flag.cpp index 7ae9c269a..43c66e38e 100644 --- a/src/Gui/Flag.cpp +++ b/src/Gui/Flag.cpp @@ -25,10 +25,10 @@ #ifndef _PreComp_ # include # include +# include #endif #include #include "View3DInventorViewer.h" -#include "GLPainter.h" #include "Flag.h" @@ -455,4 +455,76 @@ QSize FlagLayout::calculateSize(SizeType sizeType) const return totalSize; } + +TYPESYSTEM_SOURCE_ABSTRACT(Gui::GLFlagWindow, Gui::GLGraphicsItem); + +GLFlagWindow::GLFlagWindow(View3DInventorViewer* view) : _viewer(view), _flagLayout(0) +{ +} + +GLFlagWindow::~GLFlagWindow() +{ + deleteFlags(); + if (_flagLayout) + _flagLayout->deleteLater(); +} + +void GLFlagWindow::deleteFlags() +{ + if (_flagLayout) { + int ct = _flagLayout->count(); + for (int i=0; iitemAt(0)->widget(); + if (flag) { + _flagLayout->removeWidget(flag); + flag->deleteLater(); + } + } + } +} + +void GLFlagWindow::addFlag(Flag* item, FlagLayout::Position pos) +{ + if (!_flagLayout) { + _flagLayout = new FlagLayout(3); + _viewer->getGLWidget()->setLayout(_flagLayout); + } + + item->setParent(_viewer->getGLWidget()); + _flagLayout->addWidget(item, pos); + item->show(); + _viewer->scheduleRedraw(); +} + +void GLFlagWindow::removeFlag(Flag* item) +{ + if (_flagLayout) { + _flagLayout->removeWidget(item); + } +} + +void GLFlagWindow::paintGL() +{ + // draw lines for the flags + if (_flagLayout) { + // it can happen that the GL widget gets replaced internally by SoQt which + // causes to destroy the FlagLayout instance + int ct = _flagLayout->count(); + const SbViewportRegion vp = _viewer->getViewportRegion(); + SbVec2s size = vp.getViewportSizePixels(); + float aspectratio = float(size[0])/float(size[1]); + SbViewVolume vv = _viewer->getCamera()->getViewVolume(aspectratio); + for (int i=0; i(_flagLayout->itemAt(i)->widget()); + if (flag) { + SbVec3f pt = flag->getOrigin(); + vv.projectToScreen(pt, pt); + int tox = (int)(pt[0] * size[0]); + int toy = (int)((1.0f-pt[1]) * size[1]); + flag->drawLine(_viewer, tox, toy); + } + } + } +} + #include "moc_Flag.cpp" diff --git a/src/Gui/Flag.h b/src/Gui/Flag.h index 36d196a05..a5f0aaecf 100644 --- a/src/Gui/Flag.h +++ b/src/Gui/Flag.h @@ -29,9 +29,11 @@ #include #include #include +#include namespace Gui { class View3DInventorViewer; + /** * @author Werner Mayer */ @@ -98,6 +100,8 @@ private: class FlagLayout : public QLayout { + Q_OBJECT + public: enum Position { TopLeft, TopRight, BottomLeft, BottomRight }; @@ -136,6 +140,24 @@ private: QList list; }; +class GuiExport GLFlagWindow : public Gui::GLGraphicsItem +{ + TYPESYSTEM_HEADER(); + +public: + GLFlagWindow(View3DInventorViewer*); + virtual ~GLFlagWindow(); + void addFlag(Flag* item, FlagLayout::Position pos); + void removeFlag(Flag* item); + void deleteFlags(); + + void paintGL(); + +private: + View3DInventorViewer* _viewer; + FlagLayout* _flagLayout; +}; + } // namespace Gui diff --git a/src/Gui/GLPainter.cpp b/src/Gui/GLPainter.cpp index 81bffea6c..bfece7710 100644 --- a/src/Gui/GLPainter.cpp +++ b/src/Gui/GLPainter.cpp @@ -31,6 +31,8 @@ using namespace Gui; +TYPESYSTEM_SOURCE_ABSTRACT(Gui::GLGraphicsItem, Base::BaseClass); + GLPainter::GLPainter() : viewer(0), logicOp(false), lineStipple(false) { } diff --git a/src/Gui/GLPainter.h b/src/Gui/GLPainter.h index 170ce6357..287c9d1e2 100644 --- a/src/Gui/GLPainter.h +++ b/src/Gui/GLPainter.h @@ -33,6 +33,8 @@ #include #endif +#include + namespace Gui { class View3DInventorViewer; class GuiExport GLPainter @@ -71,6 +73,20 @@ private: bool lineStipple; }; +class GuiExport GLGraphicsItem : public Base::BaseClass +{ + TYPESYSTEM_HEADER(); + +public: + GLGraphicsItem() + { + } + virtual ~GLGraphicsItem() + { + } + virtual void paintGL() = 0; +}; + } // namespace Gui #endif // GUI_GLPAINTER_H diff --git a/src/Gui/Icons/freecad-icon-16.png b/src/Gui/Icons/freecad-icon-16.png new file mode 100644 index 000000000..188653a47 Binary files /dev/null and b/src/Gui/Icons/freecad-icon-16.png differ diff --git a/src/Gui/Icons/freecad-icon-32.png b/src/Gui/Icons/freecad-icon-32.png new file mode 100644 index 000000000..7ddae937d Binary files /dev/null and b/src/Gui/Icons/freecad-icon-32.png differ diff --git a/src/Gui/Icons/freecad-icon-48.png b/src/Gui/Icons/freecad-icon-48.png new file mode 100644 index 000000000..66eb0320f Binary files /dev/null and b/src/Gui/Icons/freecad-icon-48.png differ diff --git a/src/Gui/Icons/freecad-icon-64.png b/src/Gui/Icons/freecad-icon-64.png new file mode 100644 index 000000000..227f83edf Binary files /dev/null and b/src/Gui/Icons/freecad-icon-64.png differ diff --git a/src/Gui/Icons/freecadsplash.png b/src/Gui/Icons/freecadsplash.png index a0e8ad13d..f1d1ef587 100644 Binary files a/src/Gui/Icons/freecadsplash.png and b/src/Gui/Icons/freecadsplash.png differ diff --git a/src/Gui/Icons/process-stop.svg b/src/Gui/Icons/process-stop.svg new file mode 100644 index 000000000..04ce3a79f --- /dev/null +++ b/src/Gui/Icons/process-stop.svg @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Stop + 2005-10-16 + + + Andreas Nilsson + + + + + stop + halt + error + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc index e506168b6..e27da77af 100644 --- a/src/Gui/Icons/resource.qrc +++ b/src/Gui/Icons/resource.qrc @@ -49,6 +49,7 @@ edit-edit.svg help-browser.svg preferences-system.svg + process-stop.svg window-new.svg camera-photo.svg applications-accessories.svg diff --git a/src/Gui/InventorNavigationStyle.cpp b/src/Gui/InventorNavigationStyle.cpp index 1f48dd4b1..b8a3659cc 100644 --- a/src/Gui/InventorNavigationStyle.cpp +++ b/src/Gui/InventorNavigationStyle.cpp @@ -84,7 +84,7 @@ SbBool InventorNavigationStyle::processSoEvent(const SoEvent * const ev) // up the inheritance hierarchy. if (this->isSeekMode()) { return inherited::processSoEvent(ev); } // Switch off viewing mode (Bug #0000911) - if (!this->isSeekMode() && this->isViewing()) + if (!this->isSeekMode()&& !this->isAnimating() && this->isViewing() ) this->setViewing(false); // by default disable viewing mode to render the scene const SoType type(ev->getTypeId()); diff --git a/src/Gui/MDIView.cpp b/src/Gui/MDIView.cpp index 895d4ec4e..80646a14d 100644 --- a/src/Gui/MDIView.cpp +++ b/src/Gui/MDIView.cpp @@ -76,13 +76,17 @@ void MDIView::deleteSelf() { // When using QMdiArea make sure to remove the QMdiSubWindow // this view is associated with. + // + // #0001023: Crash when quitting after using Windows > Tile + // Use deleteLater() instead of delete operator. #if !defined (NO_USE_QT_MDI_AREA) QWidget* parent = this->parentWidget(); if (qobject_cast(parent)) - delete parent; + parent->deleteLater(); else #endif - delete this; + this->deleteLater(); + _pcDocument = 0; } void MDIView::setOverrideCursor(const QCursor& c) diff --git a/src/Gui/Macro.cpp b/src/Gui/Macro.cpp index 47a82ea20..e2bb6c507 100644 --- a/src/Gui/Macro.cpp +++ b/src/Gui/Macro.cpp @@ -240,8 +240,7 @@ void MacroManager::run(MacroType eType,const char *sName) throw; } catch (const Base::PyException& e) { - Base::Console().Error("%s%s: %s\n", - e.getStackTrace().c_str(), e.getErrorType().c_str(), e.what()); + e.ReportException(); } catch (const Base::Exception& e) { qWarning("%s",e.what()); diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index e1cc361a1..10ffda39b 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include # include # include # include @@ -63,6 +64,9 @@ #include "MainWindow.h" #include "Application.h" #include "Assistant.h" +#include "DownloadDialog.h" +#include "DownloadManager.h" +#include "WaitCursor.h" #include "Action.h" #include "Command.h" @@ -1172,6 +1176,8 @@ void MainWindow::delayedStartup() if (hGrp->GetBool("CreateNewDoc", false)) { App::GetApplication().newDocument(); } + + Application::Instance->checkForPreviousCrashes(); } void MainWindow::appendRecentFile(const QString& filename) @@ -1395,29 +1401,60 @@ void MainWindow::dragEnterEvent (QDragEnterEvent * e) { // Here we must allow uri drafs and check them in dropEvent const QMimeData* data = e->mimeData(); - if (data->hasUrls()) + if (data->hasUrls()) { +#if 0 +#ifdef QT_NO_OPENSSL + QList urls = data->urls(); + for (QList::ConstIterator it = urls.begin(); it != urls.end(); ++it) { + if (it->scheme().toLower() == QLatin1String("https")) { + e->ignore(); + return; + } + } +#endif +#endif e->accept(); - else + } + else { e->ignore(); + } } QMimeData * MainWindow::createMimeDataFromSelection () const { - std::vector sel = Selection().getCompleteSelection(); - unsigned int memsize=1000; // ~ for the meta-information - std::vector obj; - obj.reserve(sel.size()); - for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { - if (it->pObject) { - obj.push_back(it->pObject); - memsize += it->pObject->getMemSize(); + std::vector selobj = Selection().getCompleteSelection(); + std::map< App::Document*, std::vector > objs; + for (std::vector::iterator it = selobj.begin(); it != selobj.end(); ++it) { + if (it->pObject && it->pObject->getDocument()) { + objs[it->pObject->getDocument()].push_back(it->pObject); } } - // get a pointer to a document - if (obj.empty()) return 0; - App::Document* doc = obj.front()->getDocument(); - if (!doc) return 0; + if (objs.empty()) + return 0; + + std::vector sel; // selected + std::vector all; // object sub-graph + for (std::map< App::Document*, std::vector >::iterator it = objs.begin(); it != objs.end(); ++it) { + std::vector dep = it->first->getDependencyList(it->second); + sel.insert(sel.end(), it->second.begin(), it->second.end()); + all.insert(all.end(), dep.begin(), dep.end()); + } + + if (all.size() > sel.size()) { + int ret = QMessageBox::question(getMainWindow(), + tr("Object dependencies"), + tr("The selected objects have a dependency to unselected objects.\n" + "Do you want to copy them, too?"), + QMessageBox::Yes,QMessageBox::No); + if (ret == QMessageBox::Yes) { + sel = all; + } + } + + unsigned int memsize=1000; // ~ for the meta-information + for (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) + memsize += (*it)->getMemSize(); // if less than ~10 MB bool use_buffer=(memsize < 0xA00000); @@ -1429,22 +1466,25 @@ QMimeData * MainWindow::createMimeDataFromSelection () const use_buffer = false; } + WaitCursor wc; QString mime; if (use_buffer) { mime = QLatin1String("application/x-documentobject"); Base::ByteArrayOStreambuf buf(res); std::ostream str(&buf); // need this instance to call MergeDocuments::Save() + App::Document* doc = sel.front()->getDocument(); MergeDocuments mimeView(doc); - doc->exportObjects(obj, str); + doc->exportObjects(sel, str); } else { mime = QLatin1String("application/x-documentobject-file"); static Base::FileInfo fi(Base::FileInfo::getTempFileName()); Base::ofstream str(fi, std::ios::out | std::ios::binary); // need this instance to call MergeDocuments::Save() + App::Document* doc = sel.front()->getDocument(); MergeDocuments mimeView(doc); - doc->exportObjects(obj, str); + doc->exportObjects(sel, str); str.close(); res = fi.filePath().c_str(); } @@ -1518,6 +1558,22 @@ void MainWindow::loadUrls(App::Document* doc, const QList& url) (const char*)info.absoluteFilePath().toUtf8()); } } + else if (it->scheme().toLower() == QLatin1String("http")) { + Gui::Dialog::DownloadManager::getInstance()->download(*it); + } +//#ifndef QT_NO_OPENSSL + else if (it->scheme().toLower() == QLatin1String("https")) { + QUrl url = *it; + if (it->hasEncodedQueryItem(QByteArray("sid"))) { + url.removeEncodedQueryItem(QByteArray("sid")); + url.setScheme(QLatin1String("http")); + } + Gui::Dialog::DownloadManager::getInstance()->download(url); + } +//#endif + else if (it->scheme().toLower() == QLatin1String("ftp")) { + Gui::Dialog::DownloadManager::getInstance()->download(*it); + } } const char *docName = doc ? doc->getName() : "Unnamed"; diff --git a/src/Gui/ManualAlignment.cpp b/src/Gui/ManualAlignment.cpp index b3758a3f0..9dd0a9bcc 100644 --- a/src/Gui/ManualAlignment.cpp +++ b/src/Gui/ManualAlignment.cpp @@ -32,6 +32,7 @@ # include # include # include +# include # include # include # include diff --git a/src/Gui/ManualAlignment.h b/src/Gui/ManualAlignment.h index 78a93391f..027d90cce 100644 --- a/src/Gui/ManualAlignment.h +++ b/src/Gui/ManualAlignment.h @@ -24,6 +24,7 @@ #ifndef GUI_MANUALALIGNMENT_H #define GUI_MANUALALIGNMENT_H +#include #include #include #include diff --git a/src/Gui/MergeDocuments.cpp b/src/Gui/MergeDocuments.cpp index 9b53d960c..fe1cbbd98 100644 --- a/src/Gui/MergeDocuments.cpp +++ b/src/Gui/MergeDocuments.cpp @@ -69,7 +69,7 @@ protected: if (!propertyStack.empty()) { // replace the stored object name with the real one - if (LocalName == "Link" || (LocalName == "String" && propertyStack.top().first == "Label")) { + if (LocalName == "Link" || LocalName == "LinkSub" || (LocalName == "String" && propertyStack.top().first == "Label")) { for (std::map::iterator it = AttrMap.begin(); it != AttrMap.end(); ++it) { std::map::const_iterator jt = nameMap.find(it->second); if (jt != nameMap.end()) diff --git a/src/Gui/MouseSelection.cpp b/src/Gui/MouseSelection.cpp index dccf87142..2aec13351 100644 --- a/src/Gui/MouseSelection.cpp +++ b/src/Gui/MouseSelection.cpp @@ -53,7 +53,7 @@ AbstractMouseSelection::AbstractMouseSelection() : _pcView3D(0) void AbstractMouseSelection::grabMouseModel( Gui::View3DInventorViewer* viewer ) { - _pcView3D=viewer; + _pcView3D = viewer; m_cPrevCursor = _pcView3D->getWidget()->cursor(); // do initialization of your mousemodel @@ -62,11 +62,13 @@ void AbstractMouseSelection::grabMouseModel( Gui::View3DInventorViewer* viewer ) void AbstractMouseSelection::releaseMouseModel() { - // do termination of your mousemodel - terminate(); + if (_pcView3D) { + // do termination of your mousemodel + terminate(); - _pcView3D->getWidget()->setCursor(m_cPrevCursor); - _pcView3D = 0; + _pcView3D->getWidget()->setCursor(m_cPrevCursor); + _pcView3D = 0; + } } void AbstractMouseSelection::redraw() @@ -755,6 +757,148 @@ int RectangleSelection::keyboardEvent( const SoKeyboardEvent * const e ) // ----------------------------------------------------------------------------------- +class RubberbandSelection::Private : public Gui::GLGraphicsItem +{ + Gui::View3DInventorViewer* viewer; + int x_old, y_old, x_new, y_new; + bool working; +public: + Private(Gui::View3DInventorViewer* v) : viewer(v) + { + x_old = y_old = x_new = y_new = 0; + working = false; + } + ~Private() + { + } + void setWorking(bool on) + { + working = on; + } + void setCoords(int x1, int y1, int x2, int y2) + { + x_old = x1; + y_old = y1; + x_new = x2; + y_new = y2; + } + void paintGL() + { + if (!working) + return; + const SbViewportRegion vp = viewer->getViewportRegion(); + SbVec2s size = vp.getViewportSizePixels(); + + glMatrixMode(GL_PROJECTION); + glOrtho(0, size[0], size[1], 0, 0, 100); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glLineWidth(4.0); + glColor4f(1.0f, 1.0f, 1.0f, 0.2f); + glRecti(x_old, y_old, x_new, y_new); + glColor4f(1.0, 1.0, 0.0, 0.5); + glLineStipple(3, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + + glBegin(GL_LINE_LOOP); + glVertex2i(x_old, y_old); + glVertex2i(x_new, y_old); + glVertex2i(x_new, y_new); + glVertex2i(x_old, y_new); + glEnd(); + + glLineWidth(1.0); + glDisable(GL_LINE_STIPPLE); + glDisable(GL_BLEND); + } +}; + +RubberbandSelection::RubberbandSelection() +{ + d = 0; +} + +RubberbandSelection::~RubberbandSelection() +{ +} + +void RubberbandSelection::initialize() +{ + d = new Private(_pcView3D); + _pcView3D->addGraphicsItem(d); + _pcView3D->setRenderFramebuffer(true); + _pcView3D->scheduleRedraw(); +} + +void RubberbandSelection::terminate() +{ + _pcView3D->removeGraphicsItem(d); + delete d; d = 0; + _pcView3D->setRenderFramebuffer(false); + _pcView3D->scheduleRedraw(); +} + +void RubberbandSelection::draw () +{ +} + +int RubberbandSelection::mouseButtonEvent(const SoMouseButtonEvent * const e, const QPoint& pos) +{ + const int button = e->getButton(); + const SbBool press = e->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; + + int ret = Continue; + + if (press) { + switch (button) + { + case SoMouseButtonEvent::BUTTON1: + { + d->setWorking(true); + m_iXold = m_iXnew = pos.x(); + m_iYold = m_iYnew = pos.y(); + } break; + default: + { + } break; + } + } + else { + switch (button) { + case SoMouseButtonEvent::BUTTON1: + { + d->setWorking(false); + releaseMouseModel(); + _clPoly.push_back(e->getPosition()); + ret = Finish; + } break; + default: + { + } break; + } + } + + return ret; +} + +int RubberbandSelection::locationEvent(const SoLocation2Event * const e, const QPoint& pos) +{ + m_iXnew = pos.x(); + m_iYnew = pos.y(); + d->setCoords(m_iXold, m_iYold, m_iXnew, m_iYnew); + _pcView3D->render(); + return Continue; +} + +int RubberbandSelection::keyboardEvent(const SoKeyboardEvent * const e) +{ + return Continue; +} + +// ----------------------------------------------------------------------------------- + BoxZoomSelection::BoxZoomSelection() { } @@ -765,6 +909,8 @@ BoxZoomSelection::~BoxZoomSelection() void BoxZoomSelection::terminate() { + RubberbandSelection::terminate(); + int xmin = std::min(m_iXold, m_iXnew); int xmax = std::max(m_iXold, m_iXnew); int ymin = std::min(m_iYold, m_iYnew); diff --git a/src/Gui/MouseSelection.h b/src/Gui/MouseSelection.h index b26cdea30..70aa8548d 100644 --- a/src/Gui/MouseSelection.h +++ b/src/Gui/MouseSelection.h @@ -226,12 +226,43 @@ private: // ----------------------------------------------------------------------------------- +/** + * The selection mouse model class + * Draws a rectangle for selection + * \author Werner Mayer + */ +class GuiExport RubberbandSelection : public BaseMouseSelection +{ +public: + RubberbandSelection(); + virtual ~RubberbandSelection(); + + /// do nothing + virtual void initialize(); + /// do nothing + virtual void terminate(); + +protected: + virtual int mouseButtonEvent( const SoMouseButtonEvent * const e, const QPoint& pos ); + virtual int locationEvent ( const SoLocation2Event * const e, const QPoint& pos ); + virtual int keyboardEvent ( const SoKeyboardEvent * const e ); + + /// draw the rectangle + virtual void draw (); + +private: + class Private; + Private* d; +}; + +// ----------------------------------------------------------------------------------- + /** * The box zoom mouse model class * Draws a rectangle for box zooming * \author Werner Mayer */ -class GuiExport BoxZoomSelection : public RectangleSelection +class GuiExport BoxZoomSelection : public RubberbandSelection { public: BoxZoomSelection(); diff --git a/src/Gui/NavigationStyle.cpp b/src/Gui/NavigationStyle.cpp index 836b6f7bf..48fb8c01e 100644 --- a/src/Gui/NavigationStyle.cpp +++ b/src/Gui/NavigationStyle.cpp @@ -52,6 +52,9 @@ struct NavigationStyleP { int animationsteps; int animationdelta; SbVec3f focal1, focal2; + SbVec3f startDragPoint; + SbBool dragPointFound; + SbBool dragAtCursor; SbRotation endRotation; SoTimerSensor * animsensor; float sensitivity; @@ -62,6 +65,8 @@ struct NavigationStyleP { this->animationsteps = 0; this->sensitivity = 2.0f; this->resetcursorpos = FALSE; + this->dragPointFound = FALSE; + this->dragAtCursor = FALSE; } static void viewAnimationCB(void * data, SoSensor * sensor); }; @@ -323,6 +328,7 @@ void NavigationStyle::lookAtPoint(const SbVec3f& pos) { SoCamera* cam = viewer->getCamera(); if (cam == 0) return; + PRIVATE(this)->dragPointFound = FALSE; // Find global coordinates of focal point. SbVec3f direction; @@ -643,6 +649,7 @@ void NavigationStyle::panToCenter(const SbPlane & pplane, const SbVec2f & currpo const SbViewportRegion & vp = viewer->getViewportRegion(); float ratio = vp.getViewportAspectRatio(); panCamera(viewer->getCamera(), ratio, pplane, SbVec2f(0.5,0.5), currpos); + PRIVATE(this)->dragPointFound = FALSE; } /** Dependent on the camera type this will either shrink or expand the @@ -781,6 +788,15 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) lastpos[0] = float(this->log.position[1][0]) / float(SoQtMax((int)(glsize[0]-1), 1)); lastpos[1] = float(this->log.position[1][1]) / float(SoQtMax((int)(glsize[1]-1), 1)); + if (PRIVATE(this)->dragAtCursor && PRIVATE(this)->dragPointFound) { + SbVec3f hitpoint = PRIVATE(this)->startDragPoint; + + // set to the given position + SbVec3f direction; + viewer->getCamera()->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); + viewer->getCamera()->position = hitpoint - viewer->getCamera()->focalDistance.getValue() * direction; + } + // 0000333: Turntable camera rotation SbMatrix mat; viewer->getCamera()->orientation.getValue().getValue(mat); @@ -800,6 +816,16 @@ void NavigationStyle::spin(const SbVec2f & pointerpos) r.invert(); this->reorientCamera(viewer->getCamera(), r); + if (PRIVATE(this)->dragAtCursor && PRIVATE(this)->dragPointFound) { + float ratio = vp.getViewportAspectRatio(); + SbViewVolume vv = viewer->getCamera()->getViewVolume(vp.getViewportAspectRatio()); + SbPlane panplane = vv.getPlane(viewer->getCamera()->focalDistance.getValue()); + SbVec2f posn; + posn[0] = float(this->localPos[0]) / float(SoQtMax((int)(glsize[0]-1), 1)); + posn[1] = float(this->localPos[1]) / float(SoQtMax((int)(glsize[1]-1), 1)); + panCamera(viewer->getCamera(), ratio, panplane, posn, SbVec2f(0.5,0.5)); + } + // Calculate an average angle magnitude value to make the transition // to a possible spin animation mode appear smooth. @@ -855,6 +881,20 @@ void NavigationStyle::saveCursorPosition(const SoEvent * const ev) { this->globalPos.setValue(QCursor::pos().x(), QCursor::pos().y()); this->localPos = ev->getPosition(); + + // get the 3d point to the screen position, if possible + if (PRIVATE(this)->dragAtCursor) { + SoRayPickAction rpaction(viewer->getViewportRegion()); + rpaction.setPoint(this->localPos); + rpaction.setRadius(2); + rpaction.apply(viewer->getSceneManager()->getSceneGraph()); + + SoPickedPoint * picked = rpaction.getPickedPoint(); + if (picked) { + PRIVATE(this)->dragPointFound = TRUE; + PRIVATE(this)->startDragPoint = picked->getPoint(); + } + } } void NavigationStyle::moveCursorPosition() @@ -1028,6 +1068,9 @@ void NavigationStyle::startSelection(NavigationStyle::SelectionMode mode) case Rectangle: mouseSelection = new RectangleSelection(); break; + case Rubberband: + mouseSelection = new RubberbandSelection(); + break; case BoxZoom: mouseSelection = new BoxZoomSelection(); break; @@ -1045,8 +1088,11 @@ void NavigationStyle::startSelection(NavigationStyle::SelectionMode mode) void NavigationStyle::stopSelection() { pcPolygon.clear(); - delete mouseSelection; - mouseSelection = 0; + if (mouseSelection) { + mouseSelection->releaseMouseModel(); + delete mouseSelection; + mouseSelection = 0; + } } SbBool NavigationStyle::isSelecting() const diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 2e4900173..b801857ea 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -87,8 +87,9 @@ public: enum SelectionMode { Lasso = 0, /**< Select objects using a lasso. */ Rectangle = 1, /**< Select objects using a rectangle. */ - BoxZoom = 2, /**< Perform a box zoom. */ - Clip = 3, /**< Clip objects using a lasso. */ + Rubberband = 2, /**< Select objects using a rubberband. */ + BoxZoom = 3, /**< Perform a box zoom. */ + Clip = 4, /**< Clip objects using a lasso. */ }; enum OrbitStyle { diff --git a/src/Gui/PythonConsole.cpp b/src/Gui/PythonConsole.cpp index 3f8eb7d48..28586eb4c 100644 --- a/src/Gui/PythonConsole.cpp +++ b/src/Gui/PythonConsole.cpp @@ -130,7 +130,7 @@ InteractiveInterpreter::InteractiveInterpreter() Base::PyGILStateLocker lock; PyObject* module = PyImport_ImportModule("code"); if (!module) - throw Base::PyException(); + throw Base::PyException(); PyObject* func = PyObject_GetAttrString(module, "InteractiveInterpreter"); PyObject* args = Py_BuildValue("()"); d = new InteractiveInterpreterP; @@ -786,8 +786,10 @@ bool PythonConsole::isComment(const QString& source) const QChar ch = source.at(i++); if (ch.isSpace()) continue; - if (ch == QLatin1Char('#')) + else if (ch == QLatin1Char('#')) return true; + else + return false; } return false; @@ -1417,70 +1419,4 @@ void ConsoleHistory::doScratch( void ) // ----------------------------------------------------- -/* TRANSLATOR Gui::PythonInputField */ - -PythonInputField::PythonInputField(QWidget* parent) - : QWidget(parent) -{ - QGridLayout* gridLayout = new QGridLayout(this); - gridLayout->setSpacing(6); - gridLayout->setMargin(9); - - editField = new PythonEditor(this); - gridLayout->addWidget(editField, 0, 0, 1, 1); - setFocusProxy(editField); - - QHBoxLayout* hboxLayout = new QHBoxLayout(); - hboxLayout->setSpacing(6); - hboxLayout->setMargin(0); - - QSpacerItem* spacerItem = new QSpacerItem(131, 31, QSizePolicy::Expanding, QSizePolicy::Minimum); - hboxLayout->addItem(spacerItem); - - okButton = new QPushButton(this); - hboxLayout->addWidget(okButton); - clearButton = new QPushButton(this); - hboxLayout->addWidget(clearButton); - gridLayout->addLayout(hboxLayout, 1, 0, 1, 1); - - - this->setWindowTitle(Gui::PythonConsole::tr("Python Input Dialog")); - okButton->setText(tr("OK")); - clearButton->setText(tr("Clear")); - - QObject::connect(okButton, SIGNAL(clicked()), this, SIGNAL(textEntered())); - QObject::connect(clearButton, SIGNAL(clicked()), editField, SLOT(clear())); -} - -PythonInputField::~PythonInputField() -{ -} - -QString PythonInputField::getText() const -{ - return editField->toPlainText(); -} - -void PythonInputField::clear() -{ - return editField->clear(); -} - -void PythonInputField::changeEvent(QEvent *e) -{ - if (e->type() == QEvent::LanguageChange) { - this->setWindowTitle(Gui::PythonConsole::tr("Python Input Dialog")); - okButton->setText(tr("OK")); - clearButton->setText(tr("Clear")); - } - else { - QWidget::changeEvent(e); - } -} - -void PythonInputField::showEvent(QShowEvent* e) -{ - editField->setFocus(); -} - #include "moc_PythonConsole.cpp" diff --git a/src/Gui/PythonConsole.h b/src/Gui/PythonConsole.h index b949894d9..13efa1f45 100644 --- a/src/Gui/PythonConsole.h +++ b/src/Gui/PythonConsole.h @@ -181,29 +181,6 @@ protected: void colorChanged(const QString& type, const QColor& col); }; -class GuiExport PythonInputField : public QWidget -{ - Q_OBJECT - -public: - PythonInputField(QWidget* parent=0); - ~PythonInputField(); - QString getText() const; - void clear(); - -protected: - void changeEvent(QEvent *e); - void showEvent(QShowEvent* e); - -Q_SIGNALS: - void textEntered(); - -private: - QPushButton* okButton; - QPushButton* clearButton; - QPlainTextEdit* editField; -}; - } // namespace Gui #endif // GUI_PYTHONCONSOLE_H diff --git a/src/Gui/PythonConsolePy.cpp b/src/Gui/PythonConsolePy.cpp index b9fe2a5f0..fe28a0c6f 100644 --- a/src/Gui/PythonConsolePy.cpp +++ b/src/Gui/PythonConsolePy.cpp @@ -292,19 +292,11 @@ void PythonStdin::init_type() PythonStdin::PythonStdin(PythonConsole *pc) : pyConsole(pc) { - editField = new PythonInputField(/*getMainWindow()*/); - timer = new QTimer(); - timer->setInterval(250); - QObject::connect(timer, SIGNAL(timeout()), - editField, SLOT(hide())); console = getMainWindow()->findChild(); } PythonStdin::~PythonStdin() { - // call deleteLater() because deleting immediately causes problems - editField->deleteLater(); - timer->deleteLater(); } Py::Object PythonStdin::repr() diff --git a/src/Gui/PythonConsolePy.h b/src/Gui/PythonConsolePy.h index 63cc34284..b1ad4fcfa 100644 --- a/src/Gui/PythonConsolePy.h +++ b/src/Gui/PythonConsolePy.h @@ -30,7 +30,6 @@ class QTimer; namespace Gui { class PythonConsole; -class PythonInputField; /** * Python class for redirection of stdout to FreeCAD's Python @@ -141,9 +140,7 @@ public: Py::Object readline(const Py::Tuple&); private: - PythonInputField* editField; PythonConsole* console; - QTimer* timer; }; } // namespace Gui diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index a42da3a64..cffd9f60a 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -173,7 +173,7 @@ void SelectionObserverPython::addSelection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -192,7 +192,7 @@ void SelectionObserverPython::removeSelection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -209,7 +209,7 @@ void SelectionObserverPython::setSelection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -226,7 +226,7 @@ void SelectionObserverPython::clearSelection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -245,7 +245,7 @@ void SelectionObserverPython::setPreselection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -264,7 +264,7 @@ void SelectionObserverPython::removePreselection(const SelectionChanges& msg) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("%s\n", e.what()); + e.ReportException(); } } @@ -979,6 +979,8 @@ PyMethodDef SelectionSingleton::Methods[] = { "document is given the selection of the active document is returned.\n" "The SelectionObjects contain a variety of information about the selection,\n" "e.g. sub-element names."}, + {"getSelectionObject", (PyCFunction) SelectionSingleton::sGetSelectionObject, 1, + "getSelectionObject(doc,obj,sub,(x,y,z)) -- Return a SelectionObject"}, {"addObserver", (PyCFunction) SelectionSingleton::sAddSelObserver, 1, "addObserver(Object) -- Install an observer\n"}, {"removeObserver", (PyCFunction) SelectionSingleton::sRemSelObserver, 1, @@ -1114,6 +1116,41 @@ PyObject *SelectionSingleton::sGetSelectionEx(PyObject * /*self*/, PyObject *arg } } +PyObject *SelectionSingleton::sGetSelectionObject(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) +{ + char *docName, *objName, *subName; + PyObject* tuple=0; + if (!PyArg_ParseTuple(args, "sss|O!", &docName, &objName, &subName, + &PyTuple_Type, &tuple)) + return NULL; + + try { + SelectionObject selObj; + selObj.DocName = docName; + selObj.FeatName = objName; + std::string sub = subName; + if (!sub.empty()) { + selObj.SubNames.push_back(sub); + if (tuple) { + Py::Tuple t(tuple); + double x = (double)Py::Float(t.getItem(0)); + double y = (double)Py::Float(t.getItem(1)); + double z = (double)Py::Float(t.getItem(2)); + selObj.SelPoses.push_back(Base::Vector3d(x,y,z)); + } + } + + return selObj.getPyObject(); + } + catch (const Py::Exception&) { + return 0; + } + catch (const Base::Exception& e) { + PyErr_SetString(PyExc_Exception, e.what()); + return 0; + } +} + PyObject *SelectionSingleton::sAddSelObserver(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { PyObject* o; diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index a8e38eb8a..7aa665615 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -309,6 +309,7 @@ protected: static PyObject *sCountObjectsOfType (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetSelection (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetSelectionEx (PyObject *self,PyObject *args,PyObject *kwd); + static PyObject *sGetSelectionObject (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sAddSelObserver (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sRemSelObserver (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sAddSelectionGate (PyObject *self,PyObject *args,PyObject *kwd); diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index 93f02b8e9..692b13d23 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -47,6 +47,7 @@ #include "propertyeditor/PropertyItem.h" #include "NavigationStyle.h" +#include "Flag.h" using namespace Gui; using namespace Gui::Inventor; @@ -130,6 +131,9 @@ void Gui::SoFCDB::init() BlenderNavigationStyle ::init(); TouchpadNavigationStyle ::init(); + GLGraphicsItem ::init(); + GLFlagWindow ::init(); + qRegisterMetaType("Base::Vector3f"); qRegisterMetaType("Base::Vector3d"); init_done = TRUE; diff --git a/src/Gui/SoFCOffscreenRenderer.cpp b/src/Gui/SoFCOffscreenRenderer.cpp index 8af4a831b..025ac69d7 100644 --- a/src/Gui/SoFCOffscreenRenderer.cpp +++ b/src/Gui/SoFCOffscreenRenderer.cpp @@ -85,19 +85,16 @@ void SoFCOffscreenRenderer::writeToImage (QImage& img) const BitmapFactory().convert(image, img); } -void SoFCOffscreenRenderer::writeToImageFile (const char *filename, const char* comment) const +void SoFCOffscreenRenderer::writeToImageFile(const char* filename, const char* comment, const SbMatrix& mat, const QImage& image) { Base::FileInfo file(filename); if (file.hasExtension("JPG") || file.hasExtension("JPEG")) { - QImage img; - writeToImage(img); - // writing comment in case of jpeg (Qt ignores setText() in case of jpeg) std::string com; if (strcmp(comment,"")==0) com = "Screenshot created by FreeCAD"; else if (strcmp(comment,"$MIBA")==0) - com = createMIBA(); + com = createMIBA(mat); else com = comment; @@ -105,7 +102,7 @@ void SoFCOffscreenRenderer::writeToImageFile (const char *filename, const char* QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); - img.save(&buffer, "JPG"); + image.save(&buffer, "JPG"); writeJPEGComment(com, ba); QFile file(QString::fromUtf8(filename)); @@ -134,8 +131,7 @@ void SoFCOffscreenRenderer::writeToImageFile (const char *filename, const char* // Supported by Qt if (supported) { - QImage img; - writeToImage(img); + QImage img = image; // set keywords for PNG format if (file.hasExtension("PNG")) { img.setText(QLatin1String("Title"), QString::fromUtf8(filename)); @@ -143,7 +139,7 @@ void SoFCOffscreenRenderer::writeToImageFile (const char *filename, const char* if (strcmp(comment,"")==0) img.setText(QLatin1String("Description"), QLatin1String("Screenshot created by FreeCAD")); else if (strcmp(comment,"$MIBA")==0) - img.setText(QLatin1String("Description"), QLatin1String(createMIBA().c_str())); + img.setText(QLatin1String("Description"), QLatin1String(createMIBA(mat).c_str())); else img.setText(QLatin1String("Description"), QString::fromUtf8(comment)); img.setText(QLatin1String("Creation Time"), QDateTime::currentDateTime().toString()); @@ -169,6 +165,9 @@ void SoFCOffscreenRenderer::writeToImageFile (const char *filename, const char* throw Base::Exception(str.str()); } } + // + // Use internal buffer instead of QImage + // else if (isWriteSupported(file.extension().c_str())) { // Any format which is supported by Coin only if (!writeToFile(filename, file.extension().c_str())) @@ -249,7 +248,7 @@ QStringList SoFCOffscreenRenderer::getWriteImageFiletypeInfo() return formats; } -std::string SoFCOffscreenRenderer::createMIBA() const +std::string SoFCOffscreenRenderer::createMIBA(const SbMatrix& mat) const { std::stringstream com; const std::map& cfg = App::Application::Config(); @@ -264,10 +263,10 @@ std::string SoFCOffscreenRenderer::createMIBA() const com << " \n" ; com << " \n"; com << " \n" ; com << " \n" ; com << " \n" ; diff --git a/src/Gui/SoFCOffscreenRenderer.h b/src/Gui/SoFCOffscreenRenderer.h index a5f87b1a4..7a8cc71f4 100644 --- a/src/Gui/SoFCOffscreenRenderer.h +++ b/src/Gui/SoFCOffscreenRenderer.h @@ -79,19 +79,22 @@ public: * Note that you must still specify the full filename for the first argument, i.e. the second argument will * not automatically be attached to the filename -- it is only used to decide the filetype. * + * If \a comment is set to '$MIBA' information regarding the MIBA standard is + * embedded to the picture, otherwise the \a comment is embedded as is. + * The appropriate file format must support embedding meta information which + * is provided by JPEG or PNG. + * * This does basically the same as writeToFile() unless that all QImage file formats are supported if not * directly supported by Coin3D. */ - void writeToImageFile (const char *filename, const char* comment) const; + void writeToImageFile(const char* filename, const char* comment, const SbMatrix& mat, const QImage& img); /** * This method returns all image file formats supported by Coin3D (see getWriteFiletypeInfo()) with all QImage file formats that are * not directly supported by Coin3D, if so. */ QStringList getWriteImageFiletypeInfo(); - std::string createMIBA() const; - - SbMatrix _Matrix; + std::string createMIBA(const SbMatrix& mat) const; }; } // namespace Gui diff --git a/src/Gui/SoFCSelection.cpp b/src/Gui/SoFCSelection.cpp index f3362d4c2..282ebd98f 100644 --- a/src/Gui/SoFCSelection.cpp +++ b/src/Gui/SoFCSelection.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include # include # include #endif diff --git a/src/Gui/SoTextLabel.cpp b/src/Gui/SoTextLabel.cpp index 9fac1484c..71b29852c 100644 --- a/src/Gui/SoTextLabel.cpp +++ b/src/Gui/SoTextLabel.cpp @@ -45,6 +45,7 @@ # include #endif +#include #include #include #include @@ -55,7 +56,11 @@ #include #include #include +#if COIN_MAJOR_VERSION > 3 +#include +#else #include +#endif #include "SoTextLabel.h" #include "SoFCInteractiveElement.h" @@ -250,7 +255,11 @@ void SoTextLabel::GLRender(SoGLRenderAction *action) // disable textures for all units SoGLTextureEnabledElement::set(state, this, FALSE); +#if COIN_MAJOR_VERSION > 3 + SoMultiTextureEnabledElement::set(state, this, FALSE); +#else SoGLTexture3EnabledElement::set(state, this, FALSE); +#endif glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); @@ -285,40 +294,40 @@ void SoTextLabel::GLRender(SoGLRenderAction *action) inherited::GLRender(action); } - -// ------------------------------------------------------ - -SO_NODE_SOURCE(SoStringLabel); - -void SoStringLabel::initClass() -{ - SO_NODE_INIT_CLASS(SoStringLabel, SoNode, "Node"); -} - -SoStringLabel::SoStringLabel() -{ - SO_NODE_CONSTRUCTOR(SoStringLabel); - SO_NODE_ADD_FIELD(string, ("")); - SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f))); - SO_NODE_ADD_FIELD(name, ("Helvetica")); - SO_NODE_ADD_FIELD(size, (12)); -} - -/** - * Renders the open edges only. - */ -void SoStringLabel::GLRender(SoGLRenderAction *action) -{ - QGLWidget* window; - SoState * state = action->getState(); - state->push(); - SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); - SoGLWidgetElement::get(state, window); - if (!window) { - state->pop(); - return; - } - + +// ------------------------------------------------------ + +SO_NODE_SOURCE(SoStringLabel); + +void SoStringLabel::initClass() +{ + SO_NODE_INIT_CLASS(SoStringLabel, SoNode, "Node"); +} + +SoStringLabel::SoStringLabel() +{ + SO_NODE_CONSTRUCTOR(SoStringLabel); + SO_NODE_ADD_FIELD(string, ("")); + SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f))); + SO_NODE_ADD_FIELD(name, ("Helvetica")); + SO_NODE_ADD_FIELD(size, (12)); +} + +/** + * Renders the open edges only. + */ +void SoStringLabel::GLRender(SoGLRenderAction *action) +{ + QGLWidget* window; + SoState * state = action->getState(); + state->push(); + SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); + SoGLWidgetElement::get(state, window); + if (!window) { + state->pop(); + return; + } + // Enter in 2D screen mode glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -375,46 +384,46 @@ void SoStringLabel::GLRender(SoGLRenderAction *action) glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); - - state->pop(); -} - -// ------------------------------------------------------ - -SO_NODE_SOURCE(SoFrameLabel); - -void SoFrameLabel::initClass() -{ - SO_NODE_INIT_CLASS(SoFrameLabel, SoImage, "Image"); -} - -SoFrameLabel::SoFrameLabel() -{ - SO_NODE_CONSTRUCTOR(SoFrameLabel); - SO_NODE_ADD_FIELD(string, ("")); - SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f))); - SO_NODE_ADD_FIELD(backgroundColor, (SbVec3f(0.0f,0.333f,1.0f))); - SO_NODE_ADD_FIELD(justification, (LEFT)); - SO_NODE_ADD_FIELD(name, ("Helvetica")); - SO_NODE_ADD_FIELD(size, (12)); - SO_NODE_ADD_FIELD(frame, (TRUE)); - //SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL)); -} - -void SoFrameLabel::notify(SoNotList * list) -{ - SoField *f = list->getLastField(); - if (f == &this->string || - f == &this->textColor || - f == &this->backgroundColor || - f == &this->justification || - f == &this->name || - f == &this->size || - f == &this->frame) { - drawImage(); - } - inherited::notify(list); -} + + state->pop(); +} + +// ------------------------------------------------------ + +SO_NODE_SOURCE(SoFrameLabel); + +void SoFrameLabel::initClass() +{ + SO_NODE_INIT_CLASS(SoFrameLabel, SoImage, "Image"); +} + +SoFrameLabel::SoFrameLabel() +{ + SO_NODE_CONSTRUCTOR(SoFrameLabel); + SO_NODE_ADD_FIELD(string, ("")); + SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f))); + SO_NODE_ADD_FIELD(backgroundColor, (SbVec3f(0.0f,0.333f,1.0f))); + SO_NODE_ADD_FIELD(justification, (LEFT)); + SO_NODE_ADD_FIELD(name, ("Helvetica")); + SO_NODE_ADD_FIELD(size, (12)); + SO_NODE_ADD_FIELD(frame, (TRUE)); + //SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL)); +} + +void SoFrameLabel::notify(SoNotList * list) +{ + SoField *f = list->getLastField(); + if (f == &this->string || + f == &this->textColor || + f == &this->backgroundColor || + f == &this->justification || + f == &this->name || + f == &this->size || + f == &this->frame) { + drawImage(); + } + inherited::notify(list); +} void SoFrameLabel::drawImage() { @@ -475,24 +484,24 @@ void SoFrameLabel::drawImage() Gui::BitmapFactory().convert(image, sfimage); this->image = sfimage; } - -/** - * Renders the open edges only. - */ -void SoFrameLabel::GLRender(SoGLRenderAction *action) -{ - inherited::GLRender(action); -#if 0 - QGLWidget* window; - SoState * state = action->getState(); - state->push(); - SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); - SoGLWidgetElement::get(state, window); - if (!window) { - state->pop(); - return; - } - + +/** + * Renders the open edges only. + */ +void SoFrameLabel::GLRender(SoGLRenderAction *action) +{ + inherited::GLRender(action); +#if 0 + QGLWidget* window; + SoState * state = action->getState(); + state->push(); + SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); + SoGLWidgetElement::get(state, window); + if (!window) { + state->pop(); + return; + } + // Enter in 2D screen mode glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -549,12 +558,12 @@ void SoFrameLabel::GLRender(SoGLRenderAction *action) glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); - - state->pop(); -#endif -} - -// ------------------------------------------------------ + + state->pop(); +#endif +} + +// ------------------------------------------------------ SO_NODE_SOURCE(TranslateManip); diff --git a/src/Gui/TaskView/TaskDialogPython.cpp b/src/Gui/TaskView/TaskDialogPython.cpp index 56ca2cf22..71c9e02ea 100644 --- a/src/Gui/TaskView/TaskDialogPython.cpp +++ b/src/Gui/TaskView/TaskDialogPython.cpp @@ -193,24 +193,22 @@ TaskWatcherPython::TaskWatcherPython(const Py::Object& o) if (!tb && !title.isEmpty()) tb = new Gui::TaskView::TaskBox(icon, title, true, 0); Py::List list(watcher.getAttr(std::string("widgets"))); - Py::Module mainmod(PyImport_AddModule((char*)"sip")); - Py::Callable func = mainmod.getDict().getItem("unwrapinstance"); - for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { - Py::Tuple arguments(1); - arguments[0] = *it; //PyQt pointer - Py::Object result = func.apply(arguments); - void* ptr = PyLong_AsVoidPtr(result.ptr()); - QObject* object = reinterpret_cast(ptr); - if (object) { - QWidget* w = qobject_cast(object); - if (w) { - if (tb) - tb->groupLayout()->addWidget(w); - else - Content.push_back(w); + + Gui::PythonWrapper wrap; + if (wrap.loadCoreModule()) { + for (Py::List::iterator it = list.begin(); it != list.end(); ++it) { + QObject* object = wrap.toQObject(*it); + if (object) { + QWidget* w = qobject_cast(object); + if (w) { + if (tb) + tb->groupLayout()->addWidget(w); + else + Content.push_back(w); + } } } - } + } } if (tb) Content.push_back(tb); @@ -245,7 +243,7 @@ bool TaskWatcherPython::shouldShow() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskWatcherPython::shouldShow: %s\n", e.what()); + e.ReportException(); } if (!this->Filter.empty()) @@ -285,30 +283,28 @@ TaskDialogPython::TaskDialogPython(const Py::Object& o) : dlg(o) } } else if (dlg.hasAttr(std::string("form"))) { - Py::Object f(dlg.getAttr(std::string("form"))); - Py::List widgets; - if (f.isList()) { - widgets = f; - } - else { - widgets.append(f); - } - for (Py::List::iterator it = widgets.begin(); it != widgets.end(); ++it) { - Py::Module mainmod(PyImport_AddModule((char*)"sip")); - Py::Callable func = mainmod.getDict().getItem("unwrapinstance"); - Py::Tuple arguments(1); - arguments[0] = *it; //PyQt pointer - Py::Object result = func.apply(arguments); - void* ptr = PyLong_AsVoidPtr(result.ptr()); - QObject* object = reinterpret_cast(ptr); - if (object) { - QWidget* form = qobject_cast(object); - if (form) { - Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox( - form->windowIcon().pixmap(32), form->windowTitle(), true, 0); - taskbox->groupLayout()->addWidget(form); - Content.push_back(taskbox); - } + Py::Object f(dlg.getAttr(std::string("form"))); + Py::List widgets; + if (f.isList()) { + widgets = f; + } + else { + widgets.append(f); + } + + Gui::PythonWrapper wrap; + if (wrap.loadCoreModule()) { + for (Py::List::iterator it = widgets.begin(); it != widgets.end(); ++it) { + QObject* object = wrap.toQObject(*it); + if (object) { + QWidget* form = qobject_cast(object); + if (form) { + Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox( + form->windowIcon().pixmap(32), form->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(form); + Content.push_back(taskbox); + } + } } } } @@ -336,7 +332,7 @@ void TaskDialogPython::open() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::open: %s\n", e.what()); + e.ReportException(); } } @@ -353,7 +349,7 @@ void TaskDialogPython::clicked(int i) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::clicked: %s\n", e.what()); + e.ReportException(); } } @@ -370,7 +366,7 @@ bool TaskDialogPython::accept() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::accept: %s\n", e.what()); + e.ReportException(); } return TaskDialog::accept(); @@ -389,7 +385,7 @@ bool TaskDialogPython::reject() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::reject: %s\n", e.what()); + e.ReportException(); } return TaskDialog::reject(); @@ -407,7 +403,7 @@ void TaskDialogPython::helpRequested() } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::helpRequested: %s\n", e.what()); + e.ReportException(); } } @@ -425,7 +421,7 @@ QDialogButtonBox::StandardButtons TaskDialogPython::getStandardButtons(void) con } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::getStandardButtons: %s\n", e.what()); + e.ReportException(); } return TaskDialog::getStandardButtons(); @@ -448,7 +444,7 @@ bool TaskDialogPython::isAllowedAlterDocument(void) const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::isAllowedAlterDocument: %s\n", e.what()); + e.ReportException(); } return TaskDialog::isAllowedAlterDocument(); @@ -467,7 +463,7 @@ bool TaskDialogPython::isAllowedAlterView(void) const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::isAllowedAlterView: %s\n", e.what()); + e.ReportException(); } return TaskDialog::isAllowedAlterView(); @@ -486,7 +482,7 @@ bool TaskDialogPython::isAllowedAlterSelection(void) const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::isAllowedAlterSelection: %s\n", e.what()); + e.ReportException(); } return TaskDialog::isAllowedAlterSelection(); @@ -505,7 +501,7 @@ bool TaskDialogPython::needsFullSpace() const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("TaskDialogPython::needsFullSpace: %s\n", e.what()); + e.ReportException(); } return TaskDialog::needsFullSpace(); diff --git a/src/Gui/Thumbnail.cpp b/src/Gui/Thumbnail.cpp index aeb0a594a..ac0d4490e 100644 --- a/src/Gui/Thumbnail.cpp +++ b/src/Gui/Thumbnail.cpp @@ -29,6 +29,7 @@ # include # include # include +# include #endif #include "Thumbnail.h" @@ -85,24 +86,17 @@ void Thumbnail::SaveDocFile (Base::Writer &writer) const if (!this->viewer) return; QImage img; - try { - this->viewer->savePicture(this->size, this->size, View3DInventorViewer::Current, img); - // Alternative way of off-screen rendering -#if 0 - QGLFramebufferObject fbo(this->size, this->size,QGLFramebufferObject::Depth); - fbo.bind(); - glEnable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glDepthRange(0.1,1.0); - glEnable(GL_LINE_SMOOTH); - SoGLRenderAction gl(SbViewportRegion(this->size,this->size)); - gl.apply(this->viewer->getSceneManager()->getSceneGraph()); - fbo.release(); - img = fbo.toImage(); -#endif + if (App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Document")->GetBool("DisablePBuffers",false)) { + this->createThumbnailFromFramebuffer(img); } - catch (...) { - return; // offscreen rendering failed + else { + try { + this->viewer->savePicture(this->size, this->size, View3DInventorViewer::Current, img); + } + catch (...) { + this->createThumbnailFromFramebuffer(img); + } } QPixmap px = Gui::BitmapFactory().pixmap(App::Application::Config()["AppIcon"].c_str()); @@ -126,3 +120,11 @@ void Thumbnail::SaveDocFile (Base::Writer &writer) const void Thumbnail::RestoreDocFile(Base::Reader &reader) { } + +void Thumbnail::createThumbnailFromFramebuffer(QImage& img) const +{ + // Alternative way of off-screen rendering + QGLFramebufferObject fbo(this->size, this->size,QGLFramebufferObject::Depth); + this->viewer->renderToFramebuffer(&fbo); + img = fbo.toImage(); +} diff --git a/src/Gui/Thumbnail.h b/src/Gui/Thumbnail.h index daa66ad11..2f387a600 100644 --- a/src/Gui/Thumbnail.h +++ b/src/Gui/Thumbnail.h @@ -27,6 +27,8 @@ #include #include +class QImage; + namespace Gui { class View3DInventorViewer; @@ -53,6 +55,9 @@ public: void RestoreDocFile(Base::Reader &reader); //@} +private: + void createThumbnailFromFramebuffer(QImage&) const; + private: QUrl uri; View3DInventorViewer* viewer; diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp index cef63a384..a8552ac79 100644 --- a/src/Gui/TouchpadNavigationStyle.cpp +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -84,7 +84,7 @@ SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) // up the inheritance hierarchy. if (this->isSeekMode()) { return inherited::processSoEvent(ev); } // Switch off viewing mode (Bug #0000911) - if (!this->isSeekMode() && this->isViewing()) + if (!this->isSeekMode() && !this->isAnimating() && this->isViewing()) this->setViewing(false); // by default disable viewing mode to render the scene const SoType type(ev->getTypeId()); diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 4faa227fd..63e57a34a 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -112,6 +112,10 @@ TreeWidget::TreeWidget(QWidget* parent) this, SLOT(onTestStatus())); connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)), this, SLOT(onItemEntered(QTreeWidgetItem*))); + connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), + this, SLOT(onItemCollapsed(QTreeWidgetItem*))); + connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), + this, SLOT(onItemExpanded(QTreeWidgetItem*))); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); @@ -301,6 +305,30 @@ Qt::DropActions TreeWidget::supportedDropActions () const return QTreeWidget::supportedDropActions(); } +bool TreeWidget::event(QEvent *e) +{ +#if 0 + if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent* ke = static_cast(e); + switch (ke->key()) { + case Qt::Key_Delete: + ke->accept(); + } + } +#endif + return QTreeWidget::event(e); +} + +void TreeWidget::keyPressEvent(QKeyEvent *event) +{ +#if 0 + if (event && event->matches(QKeySequence::Delete)) { + event->ignore(); + } +#endif + QTreeWidget::keyPressEvent(event); +} + void TreeWidget::mouseDoubleClickEvent (QMouseEvent * event) { QTreeWidgetItem* item = itemAt(event->pos()); @@ -336,6 +364,15 @@ QMimeData * TreeWidget::mimeData (const QList items) const doc = obj->getDocument(); else if (doc != obj->getDocument()) return 0; + // Now check for object with a parent that is an ObjectType, too. + // If this object is *not* a group we are not allowed to remove + // its child (e.g. the sketch of a pad). + QTreeWidgetItem* parent = (*it)->parent(); + if (parent && parent->type() == TreeWidget::ObjectType) { + App::DocumentObject* par = static_cast(parent)->object()->getObject(); + if (!par->getTypeId().isDerivedFrom(App::DocumentObjectGroup::getClassTypeId())) + return 0; + } } return QTreeWidget::mimeData(items); } @@ -572,12 +609,30 @@ void TreeWidget::onTestStatus(void) void TreeWidget::onItemEntered(QTreeWidgetItem * item) { // object item selected - if ( item && item->type() == TreeWidget::ObjectType ) { + if (item && item->type() == TreeWidget::ObjectType) { DocumentObjectItem* obj = static_cast(item); obj->displayStatusInfo(); } } +void TreeWidget::onItemCollapsed(QTreeWidgetItem * item) +{ + // object item collapsed + if (item && item->type() == TreeWidget::ObjectType) { + DocumentObjectItem* obj = static_cast(item); + obj->setExpandedStatus(false); + } +} + +void TreeWidget::onItemExpanded(QTreeWidgetItem * item) +{ + // object item expanded + if (item && item->type() == TreeWidget::ObjectType) { + DocumentObjectItem* obj = static_cast(item); + obj->setExpandedStatus(true); + } +} + void TreeWidget::scrollItemToTop(Gui::Document* doc) { std::map::iterator it; @@ -810,52 +865,52 @@ void DocumentItem::slotChangeObject(const Gui::ViewProviderDocumentObject& view) std::string objectName = obj->getNameInDocument(); std::map::iterator it = ObjectMap.find(objectName); if (it != ObjectMap.end()) { - // use new grouping style - std::set children; - std::vector group = view.claimChildren(); - for (std::vector::iterator jt = group.begin(); jt != group.end(); ++jt) { - if(*jt){ - const char* internalName = (*jt)->getNameInDocument(); - if (internalName) { - std::map::iterator kt = ObjectMap.find(internalName); - if (kt != ObjectMap.end()) { - children.insert(kt->second); - QTreeWidgetItem* parent = kt->second->parent(); - if (parent && parent != it->second) { - if (it->second != kt->second) { - int index = parent->indexOfChild(kt->second); - parent->takeChild(index); - it->second->addChild(kt->second); - } - else { - Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Object references to itself.\n"); - } + // use new grouping style + std::set children; + std::vector group = view.claimChildren(); + for (std::vector::iterator jt = group.begin(); jt != group.end(); ++jt) { + if (*jt) { + const char* internalName = (*jt)->getNameInDocument(); + if (internalName) { + std::map::iterator kt = ObjectMap.find(internalName); + if (kt != ObjectMap.end()) { + children.insert(kt->second); + QTreeWidgetItem* parent = kt->second->parent(); + if (parent && parent != it->second) { + if (it->second != kt->second) { + int index = parent->indexOfChild(kt->second); + parent->takeChild(index); + it->second->addChild(kt->second); + } + else { + Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Object references to itself.\n"); } - } - else { - Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Cannot reparent unknown object.\n"); } } else { - Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Group references unknown object.\n"); + Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Cannot reparent unknown object.\n"); } } - } - // move all children which are not part of the group anymore to this item - int count = it->second->childCount(); - for (int i=0; i < count; i++) { - QTreeWidgetItem* child = it->second->child(i); - if (children.find(child) == children.end()) { - it->second->takeChild(i); - this->addChild(child); + else { + Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Group references unknown object.\n"); } } - this->treeWidget()->expandItem(it->second); + } + // move all children which are not part of the group anymore to this item + int count = it->second->childCount(); + for (int i=0; i < count; i++) { + QTreeWidgetItem* child = it->second->child(i); + if (children.find(child) == children.end()) { + it->second->takeChild(i); + this->addChild(child); + } + } // set the text label std::string displayName = obj->Label.getValue(); it->second->setText(0, QString::fromUtf8(displayName.c_str())); - } else { + } + else { Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Cannot change unknown object.\n"); } } @@ -911,8 +966,7 @@ void DocumentItem::slotHighlightObject (const Gui::ViewProviderDocumentObject& o jt->second->setData(0, Qt::BackgroundColorRole,QVariant()); break; default: - // not defined enum - assert(0); + break; } jt->second->setFont(0,f); @@ -1226,6 +1280,12 @@ void DocumentObjectItem::displayStatusInfo() } +void DocumentObjectItem::setExpandedStatus(bool on) +{ + App::DocumentObject* Obj = viewObject->getObject(); + Obj->setStatus(App::Expand, on); +} + void DocumentObjectItem::setData (int column, int role, const QVariant & value) { QTreeWidgetItem::setData(column, role, value); diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index be1126d6e..f6ce2da66 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -84,6 +84,8 @@ protected: QMimeData * mimeData (const QList items) const; void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); + bool event(QEvent *e); + void keyPressEvent(QKeyEvent *event); void mouseDoubleClickEvent(QMouseEvent * event); protected Q_SLOTS: @@ -96,6 +98,8 @@ protected Q_SLOTS: private Q_SLOTS: void onItemSelectionChanged(void); void onItemEntered(QTreeWidgetItem * item); + void onItemCollapsed(QTreeWidgetItem * item); + void onItemExpanded(QTreeWidgetItem * item); void onTestStatus(void); private: @@ -176,6 +180,7 @@ public: Gui::ViewProviderDocumentObject* object() const; void testStatus(); void displayStatusInfo(); + void setExpandedStatus(bool); void setData(int column, int role, const QVariant & value); protected: diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 01e5fa115..3fca6cf0e 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -27,6 +27,7 @@ # include # include # include +# include # include # include # include @@ -178,6 +179,12 @@ View3DInventor::~View3DInventor() delete _viewer; } +void View3DInventor::deleteSelf() +{ + _viewer->setDocument(0); + MDIView::deleteSelf(); +} + PyObject *View3DInventor::getPyObject(void) { if (!_viewerPy) @@ -483,12 +490,38 @@ void View3DInventor::print(QPrinter* printer) QImage img; QPainter p(printer); QRect rect = printer->pageRect(); - _viewer->savePicture(rect.width(), rect.height(), View3DInventorViewer::White, img); + + if (App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Document")->GetBool("DisablePBuffers",false)) { + previewFromFramebuffer(rect, img); + } + else { + try { + _viewer->savePicture(rect.width(), rect.height(), View3DInventorViewer::White, img); + } + catch (...) { + previewFromFramebuffer(rect, img); + } + } + p.drawImage(0,0,img); p.end(); #endif } +void View3DInventor::previewFromFramebuffer(const QRect& rect, QImage& img) +{ + QGLFramebufferObject fbo(rect.width(), rect.height(), QGLFramebufferObject::Depth); + const SbColor col = _viewer->getBackgroundColor(); + bool on = _viewer->hasGradientBackground(); + _viewer->setBackgroundColor(SbColor(1.0f,1.0f,1.0f)); + _viewer->setGradientBackground(false); + _viewer->renderToFramebuffer(&fbo); + _viewer->setBackgroundColor(col); + _viewer->setGradientBackground(on); + img = fbo.toImage(); +} + // ********************************************************************************** bool View3DInventor::onMsg(const char* pMsg, const char** ppReturn) diff --git a/src/Gui/View3DInventor.h b/src/Gui/View3DInventor.h index de3bc9249..f955c0cc6 100644 --- a/src/Gui/View3DInventor.h +++ b/src/Gui/View3DInventor.h @@ -73,6 +73,7 @@ public: /// Mesage handler virtual bool onMsg(const char* pMsg, const char** ppReturn); virtual bool onHasMsg(const char* pMsg) const; + virtual void deleteSelf(); /// Observer message from the ParameterGrp virtual void OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::MessageType Reason); /// get called when the document is updated @@ -127,6 +128,7 @@ protected: void focusInEvent (QFocusEvent * e); void customEvent (QEvent * e); void contextMenuEvent (QContextMenuEvent*e); + void previewFromFramebuffer(const QRect&, QImage&); /// handle to the viewer parameter group ParameterGrp::handle hGrp; diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index c53e8b566..80796f003 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -73,6 +73,7 @@ # include # include # include +# include # include # include # include @@ -109,6 +110,7 @@ #include "NavigationStyle.h" #include "ViewProvider.h" #include "SpaceballEvent.h" +#include "GLPainter.h" #include @@ -137,8 +139,8 @@ SOQT_OBJECT_ABSTRACT_SOURCE(View3DInventorViewer); View3DInventorViewer::View3DInventorViewer (QWidget *parent, const char *name, SbBool embed, Type type, SbBool build) - : inherited (parent, name, embed, type, build), editViewProvider(0),navigation(0), - editing(FALSE), redirected(FALSE), allowredir(FALSE) + : inherited (parent, name, embed, type, build), editViewProvider(0), navigation(0), + framebuffer(0), editing(FALSE), redirected(FALSE), allowredir(FALSE) { Gui::Selection().Attach(this); @@ -428,6 +430,11 @@ void View3DInventorViewer::setGradientBackground(bool on) backgroundroot->removeChild(pcBackGround); } +bool View3DInventorViewer::hasGradientBackground() const +{ + return (backgroundroot->findChild(pcBackGround) != -1); +} + void View3DInventorViewer::setGradientBackgroundColor(const SbColor& fromColor, const SbColor& toColor) { @@ -509,82 +516,6 @@ void View3DInventorViewer::setSceneGraph (SoNode *root) } } -void View3DInventorViewer::savePicture(const char* filename, int w, int h, - int eBackgroundType, const char* comment) const -{ - // if no valid color use the current background - bool useBackground = false; - SbViewportRegion vp(getViewportRegion()); - if (w>0 && h>0) - vp.setWindowSize( (short)w, (short)h ); - - //NOTE: To support pixels per inch we must use SbViewportRegion::setPixelsPerInch( ppi ); - //The default value is 72.0. - //If we need to support grayscale images with must either use SoOffscreenRenderer::LUMINANCE or - //SoOffscreenRenderer::LUMINANCE_TRANSPARENCY. - SoFCOffscreenRenderer& renderer = SoFCOffscreenRenderer::instance(); - renderer.setViewportRegion(vp); - SoCallback* cb = 0; - - // if we use transparency then we must not set a background color - switch(eBackgroundType){ - case Current: - if (backgroundroot->findChild(pcBackGround) == -1) { - renderer.setBackgroundColor(this->getBackgroundColor()); - } - else { - useBackground = true; - cb = new SoCallback; - cb->setCallback(clearBuffer); - } - break; - case White: - renderer.setBackgroundColor( SbColor(1.0, 1.0, 1.0) ); - break; - case Black: - renderer.setBackgroundColor( SbColor(0.0, 0.0, 0.0) ); - break; - case Transparent: - renderer.setComponents(SoFCOffscreenRenderer::RGB_TRANSPARENCY ); - break; - default: - break; - } - - SoSeparator* root = new SoSeparator; - root->ref(); - - SoCamera* camera = getCamera(); - if (useBackground) { - root->addChild(backgroundroot); - root->addChild(cb); - } - root->addChild(getHeadlight()); - root->addChild(camera); - SoCallback* gl = new SoCallback; - gl->setCallback(setGLWidget,this->getGLWidget()); - root->addChild(gl); - root->addChild(pcViewProviderRoot); - if (useBackground) - root->addChild(cb); - root->addChild(foregroundroot); - - try { - // render the scene - if (!renderer.render(root)) - throw Base::Exception("Offscreen rendering failed"); - // set matrix for miba - renderer._Matrix = camera->getViewVolume().getMatrix(); - //bool ok = renderer.writeToImageFile(filename, filetypeextension); - renderer.writeToImageFile(filename, comment); - root->unref(); - } - catch (...) { - root->unref(); - throw; // re-throw exception - } -} - void View3DInventorViewer::savePicture(int w, int h, int eBackgroundType, QImage& img) const { // if no valid color use the current background @@ -883,10 +814,137 @@ void View3DInventorViewer::interactionLoggerCB(void * ud, SoAction* action) Base::Console().Log("%s\n", action->getTypeId().getName().getString()); } +void View3DInventorViewer::addGraphicsItem(GLGraphicsItem* item) +{ + this->graphicsItems.push_back(item); +} + +void View3DInventorViewer::removeGraphicsItem(GLGraphicsItem* item) +{ + this->graphicsItems.remove(item); +} + +std::list View3DInventorViewer::getGraphicsItems() const +{ + return graphicsItems; +} + +std::list View3DInventorViewer::getGraphicsItemsOfType(const Base::Type& type) const +{ + std::list items; + for (std::list::const_iterator it = this->graphicsItems.begin(); it != this->graphicsItems.end(); ++it) { + if ((*it)->isDerivedFrom(type)) + items.push_back(*it); + } + return items; +} + +void View3DInventorViewer::clearGraphicsItems() +{ + this->graphicsItems.clear(); +} + +void View3DInventorViewer::setRenderFramebuffer(const SbBool enable) +{ + if (!enable) { + delete framebuffer; + framebuffer = 0; + } + else if (!this->framebuffer) { + const SbViewportRegion vp = this->getViewportRegion(); + SbVec2s origin = vp.getViewportOriginPixels(); + SbVec2s size = vp.getViewportSizePixels(); + + this->glLockNormal(); + this->framebuffer = new QGLFramebufferObject(size[0],size[1],QGLFramebufferObject::Depth); + renderToFramebuffer(this->framebuffer); + } +} + +SbBool View3DInventorViewer::isRenderFramebuffer() const +{ + return this->framebuffer != 0; +} + +void View3DInventorViewer::renderToFramebuffer(QGLFramebufferObject* fbo) +{ + this->glLockNormal(); + fbo->bind(); + int width = fbo->size().width(); + int height = fbo->size().height(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LINE_SMOOTH); + + const SbColor col = this->getBackgroundColor(); + glViewport(0, 0, width, height); + glClearColor(col[0], col[1], col[2], 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDepthRange(0.1,1.0); + + SoGLRenderAction gl(SbViewportRegion(width, height)); + gl.apply(this->backgroundroot); + gl.apply(this->getSceneManager()->getSceneGraph()); + gl.apply(this->foregroundroot); + if (this->axiscrossEnabled) { this->drawAxisCross(); } + + fbo->release(); + this->glUnlockNormal(); +} + +void View3DInventorViewer::actualRedraw() +{ + if (this->framebuffer) + renderFramebuffer(); + else + renderScene(); +} + +void View3DInventorViewer::renderFramebuffer() +{ + const SbViewportRegion vp = this->getViewportRegion(); + SbVec2s size = vp.getViewportSizePixels(); + + glDisable(GL_LIGHTING); + glViewport(0, 0, size[0], size[1]); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, this->framebuffer->texture()); + glColor3f(1.0, 1.0, 1.0); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-1.0f, 1.0f); + glEnd(); + + printDimension(); + navigation->redraw(); + for (std::list::iterator it = this->graphicsItems.begin(); it != this->graphicsItems.end(); ++it) + (*it)->paintGL(); + + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); +} + // Documented in superclass. Overrides this method to be able to draw // the axis cross, if selected, and to keep a continuous animation // upon spin. -void View3DInventorViewer::actualRedraw(void) +void View3DInventorViewer::renderScene(void) { // Must set up the OpenGL viewport manually, as upon resize // operations, Coin won't set it up until the SoGLRenderAction is @@ -935,29 +993,23 @@ void View3DInventorViewer::actualRedraw(void) // using the main portion of z-buffer again (for frontbuffer highlighting) glDepthRange(0.1,1.0); - // draw lines for the flags - if (_flaglayout) { - // it can happen that the GL widget gets replaced internally by SoQt which - // causes to destroy the FlagLayout instance - int ct = _flaglayout->count(); - SbViewVolume vv = getCamera()->getViewVolume(getGLAspectRatio()); - for (int i=0; i(_flaglayout->itemAt(i)->widget()); - if (flag) { - SbVec3f pt = flag->getOrigin(); - vv.projectToScreen(pt, pt); - int tox = (int)(pt[0] * size[0]); - int toy = (int)((1.0f-pt[1]) * size[1]); - flag->drawLine(this, tox, toy); - } - } - } - // Immediately reschedule to get continous spin animation. if (this->isAnimating()) { this->scheduleRedraw(); } +#if 0 // this breaks highlighting of edges + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); +#endif + printDimension(); navigation->redraw(); + for (std::list::iterator it = this->graphicsItems.begin(); it != this->graphicsItems.end(); ++it) + (*it)->paintGL(); + +#if 0 // this breaks highlighting of edges + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); +#endif } void View3DInventorViewer::setSeekMode(SbBool on) @@ -2056,16 +2108,3 @@ std::vector View3DInventorViewer::getViewProvidersOfType(const Ba } return views; } - -void View3DInventorViewer::addFlag(Flag* item, FlagLayout::Position pos) -{ - if (!_flaglayout) { - _flaglayout = new FlagLayout(3); - this->getGLWidget()->setLayout(_flaglayout); - } - - item->setParent(this->getGLWidget()); - _flaglayout->addWidget(item, pos); - item->show(); - this->scheduleRedraw(); -} diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 8b2e295fb..44c474761 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -24,17 +24,18 @@ #ifndef GUI_VIEW3DINVENTORVIEWER_H #define GUI_VIEW3DINVENTORVIEWER_H +#include +#include #include -#include +#include #include #include #include #include +#include #include -#include -#include class SoSeparator; @@ -45,6 +46,8 @@ class SbSphereSheetProjector; class SoEventCallback; class SbBox2s; class SoVectorizeAction; +class QGLFramebufferObject; +class QImage; namespace Gui { @@ -54,6 +57,7 @@ class NavigationStyle; class SoFCUnifiedSelection; class Document; class SoFCUnifiedSelection; +class GLGraphicsItem; /** The Inventor viewer * @@ -74,8 +78,9 @@ public: enum SelectionMode { Lasso = 0, /**< Select objects using a lasso. */ Rectangle = 1, /**< Select objects using a rectangle. */ - BoxZoom = 2, /**< Perform a box zoom. */ - Clip = 3, /**< Clip objects using a lasso. */ + Rubberband = 2, /**< Select objects using a rubberband. */ + BoxZoom = 3, /**< Perform a box zoom. */ + Clip = 4, /**< Clip objects using a lasso. */ }; /** @name Modus handling of the viewer * Here the you can switch on/off several features @@ -121,9 +126,19 @@ public: void setFeedbackSize(const int size); int getFeedbackSize(void) const; + void setRenderFramebuffer(const SbBool enable); + SbBool isRenderFramebuffer() const; + void renderToFramebuffer(QGLFramebufferObject*); + virtual void setViewing(SbBool enable); virtual void setCursorEnabled(SbBool enable); + void addGraphicsItem(GLGraphicsItem*); + void removeGraphicsItem(GLGraphicsItem*); + std::list getGraphicsItems() const; + std::list getGraphicsItemsOfType(const Base::Type&) const; + void clearGraphicsItems(); + /** @name Handling of view providers */ //@{ SbBool hasViewProvider(ViewProvider*) const; @@ -148,14 +163,8 @@ public: //@{ /** * Creates an image with width \a w and height \a h of the current scene graph - * and exports the rendered scenegraph directly to file \a filename. - * If \a comment is set to '$MIBA' information regarding the MIBA standard is - * embedded to the picture, otherwise the \a comment is embedded as is. - * The appropriate file format must support embedding meta information which - * is provided by JPEG or PNG. + * and exports the rendered scenegraph to an image. */ - void savePicture(const char* filename, int w, int h, int eBackgroundType, - const char* comment) const; void savePicture(int w, int h, int eBackgroundType, QImage&) const; void saveGraphic(int pagesize, int eBackgroundType, SoVectorizeAction* va) const; //@} @@ -255,6 +264,7 @@ public: void viewSelection(); void setGradientBackground(bool b); + bool hasGradientBackground() const; void setGradientBackgroundColor(const SbColor& fromColor, const SbColor& toColor); void setGradientBackgroundColor(const SbColor& fromColor, @@ -267,6 +277,8 @@ public: void setDocument(Gui::Document *pcDocument); protected: + void renderScene(); + void renderFramebuffer(); virtual void actualRedraw(void); virtual void setSeekMode(SbBool enable); virtual void afterRealizeHook(void); @@ -287,10 +299,15 @@ private: static void selectCB(void * closure, SoPath * p); static void deselectCB(void * closure, SoPath * p); static SoPath * pickFilterCB(void * data, const SoPickedPoint * pick); + void initialize(); + void drawAxisCross(void); + static void drawArrow(void); + void setCursorRepresentation(int mode); private: std::set _ViewProviderSet; std::map _ViewProviderMap; + std::list graphicsItems; ViewProvider* editViewProvider; SoFCBackgroundGradient *pcBackGround; SoSeparator * backgroundroot; @@ -302,27 +319,16 @@ private: SoEventCallback* pEventCallback; NavigationStyle* navigation; SoFCUnifiedSelection* selectionRoot; + QGLFramebufferObject* framebuffer; - void initialize(); SbBool axiscrossEnabled; int axiscrossSize; - void drawAxisCross(void); - static void drawArrow(void); - SbBool editing; QCursor editCursor; SbBool redirected; SbBool allowredir; - void setCursorRepresentation(int mode); - -public: - void addFlag(Flag*, FlagLayout::Position); - -private: - QPointer _flaglayout; - // friends friend class NavigationStyle; friend class GLPainter; diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index 889712108..9f008bacd 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -26,6 +26,10 @@ #ifndef __InventorAll__ # include "InventorAll.h" # include +# include +# include +# include +# include #endif @@ -37,6 +41,7 @@ #include "NavigationStyle.h" #include "SoFCSelection.h" #include "SoFCSelectionAction.h" +#include "SoFCOffscreenRenderer.h" #include "SoFCVectorizeSVGAction.h" #include "SoFCVectorizeU3DAction.h" #include "SoFCDB.h" @@ -556,6 +561,37 @@ Py::Object View3DInventorPy::isAnimationEnabled(const Py::Tuple& args) return Py::Boolean(ok ? true : false); } +void View3DInventorPy::createImageFromFramebuffer(int backgroundType, int width, int height, QImage& img) +{ + QGLFramebufferObject fbo(width, height, QGLFramebufferObject::Depth); + const SbColor col = _view->getViewer()->getBackgroundColor(); + bool on = _view->getViewer()->hasGradientBackground(); + + switch(backgroundType){ + case 0: // Current + break; + case 1: // Black + _view->getViewer()->setBackgroundColor(SbColor(0.0f,0.0f,0.0f)); + _view->getViewer()->setGradientBackground(false); + break; + case 2: // White + _view->getViewer()->setBackgroundColor(SbColor(1.0f,1.0f,1.0f)); + _view->getViewer()->setGradientBackground(false); + break; + case 3: // Transparent + _view->getViewer()->setBackgroundColor(SbColor(1.0f,1.0f,1.0f)); + _view->getViewer()->setGradientBackground(false); + break; + default: + break; + } + + _view->getViewer()->renderToFramebuffer(&fbo); + _view->getViewer()->setBackgroundColor(col); + _view->getViewer()->setGradientBackground(on); + img = fbo.toImage(); +} + Py::Object View3DInventorPy::saveImage(const Py::Tuple& args) { char *cFileName,*cImageType="Current",*cComment="$MIBA"; @@ -587,25 +623,25 @@ Py::Object View3DInventorPy::saveImage(const Py::Tuple& args) else throw Py::Exception("Parameter 4 have to be (Current|Black|White|Transparent)"); #endif + QImage img; + if (App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/Document")->GetBool("DisablePBuffers",false)) { + createImageFromFramebuffer(t, w, h, img); + } + else { + try { + _view->getViewer()->savePicture(w, h, t, img); + } + catch (const Base::Exception&) { + createImageFromFramebuffer(t, w, h, img); + } + } - try { - QColor c; - _view->getViewer()->savePicture(cFileName,w,h,t,cComment); - return Py::None(); - } - catch (const Base::Exception& e) { - Base::Console().Log("Try disabling the use of pbuffers, set the environment variables\n" - "COIN_GLXGLUE_NO_PBUFFERS=1\n" - "COIN_GLXGLUE_NO_GLX13_PBUFFERS=1\n" - "and re-run the application.\n"); - throw Py::Exception(e.what()); - } - catch (const std::exception& e) { - throw Py::Exception(e.what()); - } - catch(...) { - throw Py::Exception("Unknown C++ exception"); - } + SoFCOffscreenRenderer& renderer = SoFCOffscreenRenderer::instance(); + SoCamera* cam = _view->getViewer()->getCamera(); + renderer.writeToImageFile(cFileName, cComment, cam->getViewVolume().getMatrix(), img); + + return Py::None(); } Py::Object View3DInventorPy::saveVectorGraphic(const Py::Tuple& args) diff --git a/src/Gui/View3DPy.h b/src/Gui/View3DPy.h index 43fd65318..5b0920f59 100644 --- a/src/Gui/View3DPy.h +++ b/src/Gui/View3DPy.h @@ -28,6 +28,7 @@ #include class SoEventCallback; +class QImage; namespace Gui { @@ -102,6 +103,7 @@ private: typedef PyObject* (*method_varargs_handler)(PyObject *_self, PyObject *_args); static method_varargs_handler pycxx_handler; static PyObject *method_varargs_ext_handler(PyObject *_self, PyObject *_args); + void createImageFromFramebuffer(int backgroundType, int width, int height, QImage&); private: std::list callbacks; diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index 7bedbf042..026b6f36d 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -58,7 +58,6 @@ ViewProviderDocumentObject::ViewProviderDocumentObject() sPixmap = "Feature"; } - ViewProviderDocumentObject::~ViewProviderDocumentObject() { // Make sure that the property class does not destruct our string list @@ -139,12 +138,12 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj) // Retrieve the supported display modes of the view provider aDisplayModesArray = this->getDisplayModes(); - if( aDisplayModesArray.empty() ) + if (aDisplayModesArray.empty()) aDisplayModesArray.push_back(""); // We must collect the const char* of the strings and give it to PropertyEnumeration, // but we are still responsible for them, i.e. the property class must not delete the literals. - for ( std::vector::iterator it = aDisplayModesArray.begin(); it != aDisplayModesArray.end(); ++it ) { + for (std::vector::iterator it = aDisplayModesArray.begin(); it != aDisplayModesArray.end(); ++it) { aDisplayEnumsArray.push_back( it->c_str() ); } aDisplayEnumsArray.push_back(0); // null termination diff --git a/src/Gui/ViewProviderGeometryObject.cpp b/src/Gui/ViewProviderGeometryObject.cpp index a4394338c..698e1e608 100644 --- a/src/Gui/ViewProviderGeometryObject.cpp +++ b/src/Gui/ViewProviderGeometryObject.cpp @@ -482,13 +482,17 @@ SoPickedPoint* ViewProviderGeometryObject::getPickedPoint(const SbVec2s& pos, co void ViewProviderGeometryObject::showBoundingBox(bool show) { if (!pcBoundSwitch && show) { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + unsigned long bbcol = hGrp->GetUnsigned("BoundingBoxColor",4294967295UL); // white (255,255,255) + float r,g,b; + r = ((bbcol >> 24) & 0xff) / 255.0; g = ((bbcol >> 16) & 0xff) / 255.0; b = ((bbcol >> 8) & 0xff) / 255.0; pcBoundSwitch = new SoSwitch(); SoSeparator* pBoundingSep = new SoSeparator(); SoDrawStyle* lineStyle = new SoDrawStyle; lineStyle->lineWidth = 2.0f; pBoundingSep->addChild(lineStyle); SoBaseColor* color = new SoBaseColor(); - color->rgb.setValue(1.0f, 1.0f, 1.0f); + color->rgb.setValue(r, g, b); pBoundingSep->addChild(color); pBoundingSep->addChild(new SoTransform()); diff --git a/src/Gui/ViewProviderPythonFeature.cpp b/src/Gui/ViewProviderPythonFeature.cpp index 059131215..af668ed6a 100644 --- a/src/Gui/ViewProviderPythonFeature.cpp +++ b/src/Gui/ViewProviderPythonFeature.cpp @@ -264,7 +264,7 @@ QIcon ViewProviderPythonFeatureImp::getIcon() const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("ViewProviderPythonFeature::getIcon: %s\n", e.what()); + e.ReportException(); } return QIcon(); @@ -297,7 +297,7 @@ std::vector ViewProviderPythonFeatureImp::claimChildren(co } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - Base::Console().Error("ViewProviderPythonFeature::claimChildren: %s\n", e.what()); + e.ReportException(); } return children; @@ -342,8 +342,7 @@ bool ViewProviderPythonFeatureImp::setEdit(int ModNum) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::setEdit (%s): %s\n", name, e.what()); + e.ReportException(); } return false; @@ -378,8 +377,7 @@ bool ViewProviderPythonFeatureImp::unsetEdit(int ModNum) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::unsetEdit (%s): %s\n", name, e.what()); + e.ReportException(); } return false; @@ -414,8 +412,7 @@ void ViewProviderPythonFeatureImp::attach(App::DocumentObject *pcObject) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::attach (%s): %s\n", name, e.what()); + e.ReportException(); } } @@ -452,8 +449,7 @@ void ViewProviderPythonFeatureImp::updateData(const App::Property* prop) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::updateData (%s): %s\n", name, e.what()); + e.ReportException(); } } @@ -486,8 +482,7 @@ void ViewProviderPythonFeatureImp::onChanged(const App::Property* prop) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::onChanged (%s): %s\n", name, e.what()); + e.ReportException(); } } @@ -529,8 +524,7 @@ const char* ViewProviderPythonFeatureImp::getDefaultDisplayMode() const } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::getDefaultDisplayMode (%s): %s\n", name, e.what()); + e.ReportException(); } return 0; @@ -570,8 +564,7 @@ std::vector ViewProviderPythonFeatureImp::getDisplayModes(void) con } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::getDisplayModes (%s): %s\n", name, e.what()); + e.ReportException(); } return modes; @@ -596,8 +589,7 @@ std::string ViewProviderPythonFeatureImp::setDisplayMode(const char* ModeName) } catch (Py::Exception&) { Base::PyException e; // extract the Python error text - const char* name = object->getObject()->Label.getValue(); - Base::Console().Error("ViewProviderPythonFeature::setDisplayMode (%s): %s\n", name, e.what()); + e.ReportException(); } return ModeName; diff --git a/src/Gui/WidgetFactory.cpp b/src/Gui/WidgetFactory.cpp index fd734154f..caf4b9be2 100644 --- a/src/Gui/WidgetFactory.cpp +++ b/src/Gui/WidgetFactory.cpp @@ -23,6 +23,24 @@ #include "PreCompiled.h" +// Remove this block when activating PySide support! +#undef HAVE_SHIBOKEN +#undef HAVE_PYSIDE + +#ifdef HAVE_SHIBOKEN +# undef _POSIX_C_SOURCE +# undef _XOPEN_SOURCE +# include +# include +# include +# ifdef HAVE_PYSIDE +# include +# include +PyTypeObject** SbkPySide_QtCoreTypes=NULL; +PyTypeObject** SbkPySide_QtGuiTypes=NULL; +# endif +#endif + #include #include #include @@ -37,6 +55,91 @@ using namespace Gui; +PythonWrapper::PythonWrapper() +{ +} + +QObject* PythonWrapper::toQObject(const Py::Object& pyobject) +{ + // http://pastebin.com/JByDAF5Z +#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) + PyTypeObject * type = Shiboken::SbkType(); + if (type) { + if (Shiboken::Object::checkType(pyobject.ptr())) { + SbkObject* sbkobject = reinterpret_cast(pyobject.ptr()); + void* cppobject = Shiboken::Object::cppPointer(sbkobject, type); + return reinterpret_cast(cppobject); + } + } +#else + Py::Module mainmod(PyImport_AddModule((char*)"sip")); + Py::Callable func = mainmod.getDict().getItem("unwrapinstance"); + Py::Tuple arguments(1); + arguments[0] = pyobject; //PyQt pointer + Py::Object result = func.apply(arguments); + void* ptr = PyLong_AsVoidPtr(result.ptr()); + return reinterpret_cast(ptr); +#endif + + return 0; +} + +Py::Object PythonWrapper::fromQWidget(QWidget* widget, const char* className) +{ +#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) + PyTypeObject * type = Shiboken::SbkType(); + if (type) { + SbkObjectType* sbk_type = reinterpret_cast(type); + std::string typeName; + if (className) + typeName = className; + else + typeName = widget->metaObject()->className(); + PyObject* pyobj = Shiboken::Object::newObject(sbk_type, widget, false, false, typeName.c_str()); + return Py::asObject(pyobj); + } + throw Py::RuntimeError("Failed to wrap widget"); +#else + Py::Module sipmod(PyImport_AddModule((char*)"sip")); + Py::Callable func = sipmod.getDict().getItem("wrapinstance"); + Py::Tuple arguments(2); + arguments[0] = Py::asObject(PyLong_FromVoidPtr(widget)); + Py::Module qtmod(PyImport_ImportModule((char*)"PyQt4.Qt")); + arguments[1] = qtmod.getDict().getItem("QWidget"); + return func.apply(arguments); +#endif +} + +bool PythonWrapper::loadCoreModule() +{ +#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) + // QtCore + if (!SbkPySide_QtCoreTypes) { + Shiboken::AutoDecRef requiredModule(Shiboken::Module::import("PySide.QtCore")); + if (requiredModule.isNull()) + return false; + SbkPySide_QtCoreTypes = Shiboken::Module::getTypes(requiredModule); + } +#endif + return true; +} + +bool PythonWrapper::loadGuiModule() +{ +#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) + // QtGui + if (!SbkPySide_QtGuiTypes) { + Shiboken::AutoDecRef requiredModule(Shiboken::Module::import("PySide.QtGui")); + if (requiredModule.isNull()) + return false; + SbkPySide_QtGuiTypes = Shiboken::Module::getTypes(requiredModule); + } +#endif + return true; +} + +// ---------------------------------------------------- + Gui::WidgetFactoryInst* Gui::WidgetFactoryInst::_pcSingleton = NULL; WidgetFactoryInst& WidgetFactoryInst::instance() @@ -232,23 +335,17 @@ Py::Object UiLoaderPy::repr() Py::Object UiLoaderPy::createWidget(const Py::Tuple& args) { - Py::Module sipmod(PyImport_AddModule((char*)"sip")); - Py::Module qtmod(PyImport_ImportModule((char*)"PyQt4.Qt")); + Gui::PythonWrapper wrap; // 1st argument std::string className = (std::string)Py::String(args[0]); // 2nd argument QWidget* parent = 0; - if (args.size() > 1) { - Py::Callable func = sipmod.getDict().getItem("unwrapinstance"); - Py::Tuple arguments(1); - arguments[0] = args[1]; //PyQt pointer - Py::Object result = func.apply(arguments); - void* ptr = PyLong_AsVoidPtr(result.ptr()); - QObject* object = reinterpret_cast(ptr); - if (object) - parent = qobject_cast(object); + if (wrap.loadCoreModule() && args.size() > 1) { + QObject* object = wrap.toQObject(args[1]); + if (object) + parent = qobject_cast(object); } // 3rd argument @@ -259,11 +356,8 @@ Py::Object UiLoaderPy::createWidget(const Py::Tuple& args) QWidget* widget = loader.createWidget(QString::fromAscii(className.c_str()), parent, QString::fromAscii(objectName.c_str())); - Py::Callable func = sipmod.getDict().getItem("wrapinstance"); - Py::Tuple arguments(2); - arguments[0] = Py::asObject(PyLong_FromVoidPtr(widget)); - arguments[1] = qtmod.getDict().getItem("QWidget"); - return func.apply(arguments); + wrap.loadGuiModule(); + return wrap.fromQWidget(widget); } // ---------------------------------------------------- diff --git a/src/Gui/WidgetFactory.h b/src/Gui/WidgetFactory.h index 705e4c36b..44690d587 100644 --- a/src/Gui/WidgetFactory.h +++ b/src/Gui/WidgetFactory.h @@ -38,6 +38,18 @@ namespace Gui { namespace Dialog{ class PreferencePage; } + +class GuiExport PythonWrapper +{ +public: + PythonWrapper(); + bool loadCoreModule(); + bool loadGuiModule(); + + QObject* toQObject(const Py::Object&); + Py::Object fromQWidget(QWidget*, const char* className=0); +}; + /** * The widget factory provides methods for the dynamic creation of widgets. * To create these widgets once they must be registered to the factory. diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index 239d2399c..92180f7ca 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -136,9 +136,11 @@ void PropertyEditor::buildUp(const std::mapcurrentIndex(); QStringList propertyPath = propertyModel->propertyPathFromIndex(index); + if (!propertyPath.isEmpty()) + this->selectedProperty = propertyPath; propertyModel->buildUp(props); - if (!propertyPath.isEmpty()) { - QModelIndex index = propertyModel->propertyIndexFromPath(propertyPath); + if (!this->selectedProperty.isEmpty()) { + QModelIndex index = propertyModel->propertyIndexFromPath(this->selectedProperty); this->setCurrentIndex(index); } } diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index 6e59d9631..aa9c89587 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -63,6 +63,7 @@ protected: private: PropertyModel* propertyModel; + QStringList selectedProperty; bool autoupdate; bool committing; bool delaybuild; diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index edb518ba4..c542dc921 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -2111,9 +2111,24 @@ QVariant PropertyLinkItem::value(const App::Property* prop) const const App::PropertyLink* prop_link = static_cast(prop); App::DocumentObject* obj = prop_link->getValue(); QStringList list; - list << QString::fromAscii(obj->getDocument()->getName()); - list << QString::fromAscii(obj->getNameInDocument()); - list << QString::fromUtf8(obj->Label.getValue()); + if (obj) { + list << QString::fromAscii(obj->getDocument()->getName()); + list << QString::fromAscii(obj->getNameInDocument()); + list << QString::fromUtf8(obj->Label.getValue()); + } + else { + App::PropertyContainer* c = prop_link->getContainer(); + if (c->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) { + App::DocumentObject* obj = static_cast(c); + list << QString::fromAscii(obj->getDocument()->getName()); + } + else { + list << QString::fromAscii(""); + } + list << QString::fromAscii("Null"); + list << QString::fromAscii(""); + } + return QVariant(list); } diff --git a/src/Main/MainGui.cpp b/src/Main/MainGui.cpp index 03407c52d..fdb417da2 100644 --- a/src/Main/MainGui.cpp +++ b/src/Main/MainGui.cpp @@ -161,6 +161,10 @@ private: QDomDocument domDocument; }; +#if defined(_MSC_VER) +void InitMiniDumpWriter(const std::string&); +#endif + #if defined (FC_OS_LINUX) || defined(FC_OS_BSD) QString myDecoderFunc(const QByteArray &localFileName) { @@ -224,6 +228,12 @@ int main( int argc, char ** argv ) // Inits the Application App::Application::init(argc,argv); +#if defined(_MSC_VER) + // create a dump file when the application crashes + std::string dmpfile = App::Application::getUserAppDataDir(); + dmpfile += "crash.dmp"; + InitMiniDumpWriter(dmpfile); +#endif Gui::Application::initApplication(); Base::Interpreter().replaceStdOutput(); } @@ -325,3 +335,99 @@ int main( int argc, char ** argv ) return 0; } + +#if defined(_MSC_VER) +#include +#include + +typedef BOOL (__stdcall *tMDWD)( + IN HANDLE hProcess, + IN DWORD ProcessId, + IN HANDLE hFile, + IN MINIDUMP_TYPE DumpType, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL + IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL + IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL + ); + +static tMDWD s_pMDWD; +static HMODULE s_hDbgHelpMod; +static MINIDUMP_TYPE s_dumpTyp = MiniDumpNormal; +static std::string s_szMiniDumpFileName; // initialize with whatever appropriate... + +static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS* pEx) +{ +#ifdef _M_IX86 + if (pEx->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) + { + // be sure that we have enought space... + static char MyStack[1024*128]; + // it assumes that DS and SS are the same!!! (this is the case for Win32) + // change the stack only if the selectors are the same (this is the case for Win32) + //__asm push offset MyStack[1024*128]; + //__asm pop esp; + __asm mov eax,offset MyStack[1024*128]; + __asm mov esp,eax; + } +#endif + bool bFailed = true; + HANDLE hFile; + hFile = CreateFile(s_szMiniDumpFileName.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + MINIDUMP_EXCEPTION_INFORMATION stMDEI; + stMDEI.ThreadId = GetCurrentThreadId(); + stMDEI.ExceptionPointers = pEx; + stMDEI.ClientPointers = TRUE; + // try to create an miniDump: + if (s_pMDWD( + GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + s_dumpTyp, + &stMDEI, + NULL, + NULL + )) + { + bFailed = false; // suceeded + } + CloseHandle(hFile); + } + + if (bFailed) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Optional display an error message + // FatalAppExit(-1, ("Application failed!")); + + + // or return one of the following: + // - EXCEPTION_CONTINUE_SEARCH + // - EXCEPTION_CONTINUE_EXECUTION + // - EXCEPTION_EXECUTE_HANDLER + return EXCEPTION_CONTINUE_SEARCH; // this will trigger the "normal" OS error-dialog +} + +void InitMiniDumpWriter(const std::string& filename) +{ + if (s_hDbgHelpMod != NULL) + return; + s_szMiniDumpFileName = filename; + + // Initialize the member, so we do not load the dll after the exception has occured + // which might be not possible anymore... + s_hDbgHelpMod = LoadLibrary(("dbghelp.dll")); + if (s_hDbgHelpMod != NULL) + s_pMDWD = (tMDWD) GetProcAddress(s_hDbgHelpMod, "MiniDumpWriteDump"); + + // Register Unhandled Exception-Filter: + SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter); + + // Additional call "PreventSetUnhandledExceptionFilter"... + // See also: "SetUnhandledExceptionFilter" and VC8 (and later) + // http://blog.kalmbachnet.de/?postid=75 +} +#endif diff --git a/src/Mod/Arch/Arch.py b/src/Mod/Arch/Arch.py index fc537d85a..77c0b9534 100644 --- a/src/Mod/Arch/Arch.py +++ b/src/Mod/Arch/Arch.py @@ -30,7 +30,6 @@ import FreeCADGui FreeCADGui.updateLocale() from ArchWall import * -from ArchCell import * from ArchFloor import * from ArchSite import * from ArchBuilding import * diff --git a/src/Mod/Arch/ArchCell.py b/src/Mod/Arch/ArchCell.py deleted file mode 100644 index 73fa2d21f..000000000 --- a/src/Mod/Arch/ArchCell.py +++ /dev/null @@ -1,155 +0,0 @@ -#*************************************************************************** -#* * -#* Copyright (c) 2011 * -#* Yorik van Havre * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with this program; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** - -#### WARNING: CELL OBJECT IS OBSOLETED - -import FreeCAD,FreeCADGui,Draft,ArchComponent,ArchCommands -from FreeCAD import Vector -from PyQt4 import QtCore - -__title__="FreeCAD Cell" -__author__ = "Yorik van Havre" -__url__ = "http://free-cad.sourceforge.net" - -def makeCell(objectslist=None,join=True,name="Cell"): - '''makeCell([objectslist],[joinmode]): creates a cell including the - objects from the given list. If joinmode is False, contents will - not be joined.''' - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) - _Cell(obj) - _ViewProviderCell(obj.ViewObject) - if objectslist: - obj.Components = objectslist - for comp in obj.Components: - comp.ViewObject.hide() - obj.JoinMode = join - return obj - -class _CommandCell: - "the Arch Cell command definition" - def GetResources(self): - return {'Pixmap' : 'Arch_Cell', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Cell","Cell"), - 'Accel': "C, E", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Cell","Creates a cell object including selected objects")} - - def Activated(self): - sel = FreeCADGui.Selection.getSelection() - ok = False - if (len(sel) == 1): - if Draft.getType(sel[0]) in ["Floor","Site","Building"]: - FreeCAD.ActiveDocument.openTransaction("Type conversion") - nobj = makeCell() - ArchCommands.copyProperties(sel[0],nobj) - FreeCAD.ActiveDocument.removeObject(sel[0].Name) - FreeCAD.ActiveDocument.commitTransaction() - ok = True - if not ok: - FreeCAD.ActiveDocument.openTransaction("Cell") - makeCell(sel) - FreeCAD.ActiveDocument.commitTransaction() - FreeCAD.ActiveDocument.recompute() - -class _Cell(ArchComponent.Component): - "The Cell object" - def __init__(self,obj): - obj.addProperty("App::PropertyLinkList","Components","Base", - "The objects that make part of this cell") - obj.addProperty("App::PropertyBool","JoinMode","Base", - "If True, underlying geometry will be joined") - obj.Proxy = self - self.Type = "Cell" - - def execute(self,obj): - self.createGeometry(obj) - - def onChanged(self,obj,prop): - if prop in ["Components","JoinMode"]: - self.createGeometry(obj) - - def createGeometry(self,obj): - import Part - pl = obj.Placement - if obj.Components: - shapes = [] - if obj.JoinMode: - walls = [] - structs = [] - for c in obj.Components: - if Draft.getType(c) == "Wall": - walls.append(c.Shape) - elif Draft.getType(c) == "Structure": - structs.append(c.Shape) - else: - shapes.append(c.Shape) - for group in [walls,structs]: - if group: - sh = group.pop(0).copy() - for subsh in group: - sh = sh.oldFuse(subsh) - shapes.append(sh) - else: - for c in obj.Components: - shapes.append(c.Shape) - if shapes: - obj.Shape = Part.makeCompound(shapes) - obj.Placement = pl - -class _ViewProviderCell(ArchComponent.ViewProviderComponent): - "A View Provider for the Cell object" - def __init__(self,vobj): - ArchComponent.ViewProviderComponent.__init__(self,vobj) - vobj.Proxy = self - self.Object = vobj.Object - - def getIcon(self): - import Arch_rc - return ":/icons/Arch_Cell_Tree.svg" - - def updateData(self,obj,prop): - return - - def onChanged(self,vobj,prop): - return - - def claimChildren(self): - return self.Object.Components - - def attach(self,vobj): - self.Object = vobj.Object - return - - def getDisplayModes(self,obj): - modes=[] - return modes - - def setDisplayMode(self,mode): - return mode - - def __getstate__(self): - return None - - def __setstate__(self,state): - return None - -FreeCADGui.addCommand('Arch_Cell',_CommandCell()) diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 52cf96397..8212772fa 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -45,14 +45,16 @@ def getStringList(objects): def getDefaultColor(objectType): '''getDefaultColor(string): returns a color value for the given object - type (Wall, Structure, Window)''' + type (Wall, Structure, Window, WindowGlass)''' p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") if objectType == "Wall": - c = p.GetUnsigned("WallColor") + c = p.GetUnsigned("WallColor",4294967295) elif objectType == "Structure": - c = p.GetUnsigned("StructureColor") + c = p.GetUnsigned("StructureColor",2847259391) + elif objectType == "WindowGlass": + c = p.GetUnsigned("WindowGlassColor",1772731135) else: - c = p.GetUnsigned("WindowsColor") + c = p.GetUnsigned("WindowsColor",810781695) r = float((c>>24)&0xFF)/255.0 g = float((c>>16)&0xFF)/255.0 b = float((c>>8)&0xFF)/255.0 @@ -523,6 +525,19 @@ def check(objectslist,includehidden=False): bad.append([o,str(translate("Arch","contains faces that are not part of any solid"))]) return bad + +def addFixture(fixture,baseobject): + '''addFixture(fixture,baseobject): adds the given object as a + fixture to the given base object''' + if hasattr(baseobject,"Fixtures"): + f = baseobject.Fixtures + f.append(fixture) + baseobject.Fixtures = f + if baseobject.ViewObject.DisplayMode != "Detailed": + fixture.ViewObject.hide() + else: + FreeCAD.Console.PrintMessage(str(translate("Arch","This object has no support for fixtures"))) + # command definitions ############################################### @@ -735,6 +750,30 @@ class _CommandCheck: FreeCADGui.Selection.addSelection(i[0]) +class _CommandFixture: + "the Arch Fixture command definition" + def GetResources(self): + return {'Pixmap' : 'Arch_Fixture', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Fixture","Add fixture"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Fixture","Adds the selected components as fixtures to the active object")} + + def IsActive(self): + if len(FreeCADGui.Selection.getSelection()) > 1: + return True + else: + return False + + def Activated(self): + sel = FreeCADGui.Selection.getSelection() + FreeCAD.ActiveDocument.openTransaction(str(translate("Arch","Grouping"))) + host = sel.pop() + for o in sel: + FreeCADGui.doCommand("import Arch") + FreeCADGui.doCommand("Arch.addFixture(FreeCAD.ActiveDocument."+o.Name+",FreeCAD.ActiveDocument."+host.Name+")") + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() + + FreeCADGui.addCommand('Arch_Add',_CommandAdd()) FreeCADGui.addCommand('Arch_Remove',_CommandRemove()) FreeCADGui.addCommand('Arch_SplitMesh',_CommandSplitMesh()) @@ -743,3 +782,4 @@ FreeCADGui.addCommand('Arch_SelectNonSolidMeshes',_CommandSelectNonSolidMeshes() FreeCADGui.addCommand('Arch_RemoveShape',_CommandRemoveShape()) FreeCADGui.addCommand('Arch_CloseHoles',_CommandCloseHoles()) FreeCADGui.addCommand('Arch_Check',_CommandCheck()) +FreeCADGui.addCommand('Arch_Fixture',_CommandFixture()) diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index aa5a30064..786f48914 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -74,6 +74,7 @@ def addToComponent(compobject,addobject,mod=None): addobject.ViewObject.hide() break + def removeFromComponent(compobject,subobject): '''removeFromComponent(compobject,subobject): subtracts subobject from the given component. If the subobject is already part of the @@ -81,7 +82,7 @@ def removeFromComponent(compobject,subobject): it is added as a subtraction.''' if compobject == subobject: return found = False - attribs = ["Additions","Subtractions","Objects","Components","Base","Axes"] + attribs = ["Additions","Subtractions","Objects","Components","Base","Axes","Fixtures"] for a in attribs: if hasattr(compobject,a): if a == "Base": @@ -113,7 +114,7 @@ class ComponentTaskPanel: # the categories are shown only if they are not empty. self.obj = None - self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes"] + self.attribs = ["Base","Additions","Subtractions","Objects","Components","Axes","Fixtures"] self.form = QtGui.QWidget() self.form.setObjectName("TaskPanel") self.grid = QtGui.QGridLayout(self.form) @@ -263,6 +264,8 @@ class Component: "Other shapes that are appended to this object") obj.addProperty("App::PropertyLinkList","Subtractions","Base", "Other shapes that are subtracted from this object") + obj.addProperty("App::PropertyLinkList","Fixtures","Base", + "Shapes or Meshes that are appended to this object without modifying its geometry") obj.Proxy = self self.Type = "Component" self.Subvolume = None @@ -275,7 +278,18 @@ class Component: def __setstate__(self,state): if state: self.Type = state - + + def onChanged(self,obj,prop): + if prop == "Placement": + # make fixtures move along with host + if hasattr(obj,"Fixtures"): + vo = obj.Shape.Placement.Base + vn = obj.Placement.Base + import DraftVecUtils + if not DraftVecUtils.equals(vo,vn): + delta = vn.sub(vo) + for o in obj.Fixtures: + o.Placement.move(delta) def getSubVolume(self,base,width,plac=None): "returns a subvolume from a base object" @@ -388,7 +402,6 @@ class Component: base = base.cut(o.Shape) return base - class ViewProviderComponent: "A default View Provider for Component objects" def __init__(self,vobj): @@ -406,11 +419,22 @@ class ViewProviderComponent: return def getDisplayModes(self,vobj): - modes=[] + modes=["Detailed"] return modes def setDisplayMode(self,mode): - return mode + if mode == "Detailed": + if hasattr(self,"Object"): + if hasattr(self.Object,"Fixtures"): + for f in self.Object.Fixtures: + f.ViewObject.show() + return "Flat Lines" + else: + if hasattr(self,"Object"): + if hasattr(self.Object,"Fixtures"): + for f in self.Object.Fixtures: + f.ViewObject.hide() + return mode def __getstate__(self): return None @@ -419,7 +443,10 @@ class ViewProviderComponent: return None def claimChildren(self): - return [self.Object.Base]+self.Object.Additions+self.Object.Subtractions + c = [self.Object.Base]+self.Object.Additions+self.Object.Subtractions + if hasattr(self.Object,"Fixtures"): + c.extend(self.Object.Fixtures) + return c def setEdit(self,vobj,mode): taskd = ComponentTaskPanel() diff --git a/src/Mod/Arch/ArchStructure.py b/src/Mod/Arch/ArchStructure.py index 14a9a3f80..379730903 100644 --- a/src/Mod/Arch/ArchStructure.py +++ b/src/Mod/Arch/ArchStructure.py @@ -97,6 +97,9 @@ class _CommandStructure: import DraftTrackers self.points = [] self.tracker = DraftTrackers.boxTracker() + self.tracker.width(self.Width) + self.tracker.height(self.Height) + self.tracker.length(self.Length) self.tracker.on() FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox()) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 91d3f26f6..5f0ca5cb0 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -136,6 +136,7 @@ class _CommandWall: self.Width = 0.1 self.Height = 1 self.Align = "Center" + self.Length = None self.continueCmd = False p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") self.JOIN_WALLS = p.GetBool("joinWallSketches") @@ -223,12 +224,24 @@ class _CommandWall: else: dv = DraftVecUtils.neg(dv) self.tracker.update([b.add(dv),point.add(dv)]) + if self.Length: + self.Length.setValue(bv.Length) def taskbox(self): "sets up a taskbox widget" w = QtGui.QWidget() w.setWindowTitle(str(translate("Arch","Wall options"))) lay0 = QtGui.QVBoxLayout(w) + + lay5 = QtGui.QHBoxLayout() + lay0.addLayout(lay5) + label5 = QtGui.QLabel(str(translate("Arch","Length"))) + lay5.addWidget(label5) + self.Length = QtGui.QDoubleSpinBox() + self.Length.setDecimals(2) + self.Length.setValue(0.00) + lay5.addWidget(self.Length) + lay1 = QtGui.QHBoxLayout() lay0.addLayout(lay1) label1 = QtGui.QLabel(str(translate("Arch","Width"))) @@ -237,6 +250,7 @@ class _CommandWall: value1.setDecimals(2) value1.setValue(self.Width) lay1.addWidget(value1) + lay2 = QtGui.QHBoxLayout() lay0.addLayout(lay2) label2 = QtGui.QLabel(str(translate("Arch","Height"))) @@ -245,6 +259,7 @@ class _CommandWall: value2.setDecimals(2) value2.setValue(self.Height) lay2.addWidget(value2) + lay3 = QtGui.QHBoxLayout() lay0.addLayout(lay3) label3 = QtGui.QLabel(str(translate("Arch","Alignment"))) @@ -254,6 +269,7 @@ class _CommandWall: value3.addItems(items) value3.setCurrentIndex(items.index(self.Align)) lay3.addWidget(value3) + value4 = QtGui.QCheckBox(str(translate("Arch","Continue"))) lay0.addWidget(value4) QtCore.QObject.connect(value1,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) @@ -346,9 +362,8 @@ class _Wall(ArchComponent.Component): for o in obj.OutList: if (Draft.getType(o) == "Window") or Draft.isClone(o,"Window"): o.Placement.move(delta) + ArchComponent.Component.onChanged(self,obj,prop) - - def getDefaultValues(self,obj): "returns normal,width,height values from this wall" width = 1.0 @@ -492,14 +507,14 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent): return ":/icons/Arch_Wall_Tree.svg" def getDisplayModes(self,vobj): - return ["Flat 2D"] + return ArchComponent.ViewProviderComponent.getDisplayModes(self,vobj)+["Flat 2D"] def setDisplayMode(self,mode): self.Object.Proxy.createGeometry(self.Object) if mode == "Flat 2D": return "Flat Lines" else: - return mode + return ArchComponent.ViewProviderComponent.setDisplayMode(self,mode) def attach(self,vobj): self.Object = vobj.Object diff --git a/src/Mod/Arch/ArchWindow.py b/src/Mod/Arch/ArchWindow.py index 9768caf00..c3d72f30a 100644 --- a/src/Mod/Arch/ArchWindow.py +++ b/src/Mod/Arch/ArchWindow.py @@ -30,6 +30,8 @@ __title__="FreeCAD Wall" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" +WindowPartTypes = ["Frame","Solid panel","Glass panel"] + def makeWindow(baseobj=None,width=None,name=str(translate("Arch","Window"))): '''makeWindow(obj,[name]): creates a window based on the given object''' @@ -64,7 +66,7 @@ def makeDefaultWindowPart(obj): if ws: ws += "," ws += "Wire" + str(i) i += 1 - part = ["Default","Panel",ws,"1","0"] + part = ["Default","Frame",ws,"1","0"] return part class _CommandWindow: @@ -183,6 +185,15 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): def getIcon(self): import Arch_rc return ":/icons/Arch_Window_Tree.svg" + + def updateData(self,obj,prop): + if (prop in ["WindowParts","Shape"]) and obj.ViewObject: + self.colorize(obj) + + def onChanged(self,vobj,prop): + if (prop == "DiffuseColor") and vobj.Object: + if len(vobj.DiffuseColor) < 2: + self.colorize(vobj.Object) def setEdit(self,vobj,mode): taskd = _ArchWindowTaskPanel() @@ -202,7 +213,24 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): if self.Object.Base: self.Object.Base.ViewObject.hide() FreeCADGui.Control.closeDialog() - return + return False + + def colorize(self,obj): + "setting different part colors" + print "Colorizing ", obj.Shape.Solids + colors = [] + base = obj.ViewObject.ShapeColor + for i in range(len(obj.Shape.Solids)): + ccol = base + typeidx = (i*5)+1 + if typeidx < len(obj.WindowParts): + typ = obj.WindowParts[typeidx] + if typ == WindowPartTypes[2]: # transparent parts + ccol = ArchCommands.getDefaultColor("WindowGlass") + for f in obj.Shape.Solids[i].Faces: + colors.append(ccol) + print "colors: ",colors + obj.ViewObject.DiffuseColor = colors class _ArchWindowTaskPanel: '''The TaskPanel for Arch Windows''' @@ -262,10 +290,10 @@ class _ArchWindowTaskPanel: self.new4 = QtGui.QLabel(self.form) self.new5 = QtGui.QLabel(self.form) self.field1 = QtGui.QLineEdit(self.form) - self.field2 = QtGui.QLineEdit(self.form) + self.field2 = QtGui.QComboBox(self.form) self.field3 = QtGui.QLineEdit(self.form) self.field4 = QtGui.QLineEdit(self.form) - self.field5 = QtGui.QLineEdit(self.form) + self.field5 = QtGui.QLineEdit(self.form) self.createButton = QtGui.QPushButton(self.form) self.createButton.setObjectName("createButton") self.createButton.setIcon(QtGui.QIcon(":/icons/Arch_Add.svg")) @@ -289,6 +317,8 @@ class _ArchWindowTaskPanel: self.new5.setVisible(False) self.field1.setVisible(False) self.field2.setVisible(False) + for t in WindowPartTypes: + self.field2.addItem("") self.field3.setVisible(False) self.field3.setReadOnly(True) self.field4.setVisible(False) @@ -370,18 +400,17 @@ class _ArchWindowTaskPanel: def addElement(self): 'opens the component creation dialog' self.field1.setText('') - self.field2.setText('') self.field3.setText('') self.field4.setText('') self.field5.setText('') self.newtitle.setVisible(True) self.new1.setVisible(True) - #self.new2.setVisible(True) + self.new2.setVisible(True) self.new3.setVisible(True) self.new4.setVisible(True) self.new5.setVisible(True) self.field1.setVisible(True) - #self.field2.setVisible(True) + self.field2.setVisible(True) self.field3.setVisible(True) self.field4.setVisible(True) self.field5.setVisible(True) @@ -414,7 +443,14 @@ class _ArchWindowTaskPanel: for i in range(5): f = getattr(self,"field"+str(i+1)) t = self.obj.WindowParts[ind+i] - f.setText(t) + if i == 1: + # special behaviour for types + if t in WindowPartTypes: + f.setCurrentIndex(WindowPartTypes.index(t)) + else: + f.setCurrentIndex(0) + else: + f.setText(t) def create(self): 'adds a new component' @@ -422,7 +458,17 @@ class _ArchWindowTaskPanel: ok = True ar = [] for i in range(5): - t = str(getattr(self,"field"+str(i+1)).text()) + if i == 1: + n = getattr(self,"field"+str(i+1)).currentIndex() + if n in range(len(WindowPartTypes)): + t = WindowPartTypes[n] + else: + # if type was not specified or is invalid, we set a default + t = WindowPartTypes[0] + else: + t = str(getattr(self,"field"+str(i+1)).text()) + if t in WindowPartTypes: + t = t + "_" # avoiding part names similar to types if t == "": if not(i in [1,5]): ok = False @@ -483,5 +529,7 @@ class _ArchWindowTaskPanel: self.new3.setText(QtGui.QApplication.translate("Arch", "Wires", None, QtGui.QApplication.UnicodeUTF8)) self.new4.setText(QtGui.QApplication.translate("Arch", "Thickness", None, QtGui.QApplication.UnicodeUTF8)) self.new5.setText(QtGui.QApplication.translate("Arch", "Z offset", None, QtGui.QApplication.UnicodeUTF8)) + for i in range(len(WindowPartTypes)): + self.field2.setItemText(i, QtGui.QApplication.translate("Arch", WindowPartTypes[i], None, QtGui.QApplication.UnicodeUTF8)) FreeCADGui.addCommand('Arch_Window',_CommandWindow()) diff --git a/src/Mod/Arch/Arch_rc.py b/src/Mod/Arch/Arch_rc.py index 2bc77e48c..37b7e01be 100644 --- a/src/Mod/Arch/Arch_rc.py +++ b/src/Mod/Arch/Arch_rc.py @@ -2,8 +2,8 @@ # Resource object code # -# Created: Wed Apr 17 16:59:58 2013 -# by: The Resource Compiler for PyQt (Qt v4.8.2) +# Created: Sun Jul 7 11:06:17 2013 +# by: The Resource Compiler for PyQt (Qt v4.8.4) # # WARNING! All changes made in this file will be lost! @@ -27568,130 +27568,133 @@ qt_resource_data = "\ \x6c\x6c\x61\x64\x61\x20\x73\x75\x70\x70\x6f\x72\x74\x20\x77\x69\ \x6c\x6c\x20\x62\x65\x20\x64\x69\x73\x61\x62\x6c\x65\x64\x2e\x0a\ \x07\x00\x00\x00\x04\x61\x72\x63\x68\x01\ -\x00\x00\x07\x98\ +\x00\x00\x07\xcc\ \x00\ -\x00\x38\x4e\x78\x9c\xed\x5b\x6d\x73\xd3\x38\x10\xfe\xce\xaf\xd0\ -\xe4\xd3\xdd\x0c\x90\x36\xf4\x85\x76\xdc\x30\x40\x29\xf4\x06\x8e\ -\x72\xe9\xc1\x47\x46\xb1\x37\xb1\xa8\x2c\xf9\x24\xb9\x49\xf8\xf5\ -\xb7\x92\xed\x38\x8e\x1d\xb7\x69\x5e\x3a\xd3\x09\x03\x83\xad\x55\ -\x76\x57\xeb\xdd\x67\x77\x65\xd9\x7b\x33\x8e\x38\xb9\x05\xa5\x99\ -\x14\x67\xad\xfd\x97\x7b\x2d\x02\xc2\x97\x01\x13\xc3\xb3\xd6\xbf\ -\xd7\x17\x2f\x5e\xb7\xde\x74\x9f\x79\x09\x2b\x26\x1d\xe0\xa4\xee\ -\x33\xe2\xf9\x9c\x6a\xdd\xfd\x98\xb0\xd3\xd3\x73\x46\xb9\x1c\xe2\ -\xff\x7c\xd8\x03\x63\xf0\xc7\xfa\xad\xf2\x43\xaf\x9d\xce\xc1\xc9\ -\x23\x16\x0c\xc1\x10\x77\x7f\xd6\xfa\xf6\xc3\xdd\xb6\x88\xa0\x11\ -\x9c\xb5\x9a\x78\x58\x51\xc4\x8b\x95\x8c\x41\x99\x49\xf6\x83\x21\ -\xc8\x08\x8c\x9a\x38\x22\xf1\x14\xf8\xc6\x5d\x11\x6f\xdc\xdd\xf3\ -\xda\xe3\xec\x66\x62\x6f\x26\xd9\x0d\x6a\x60\xc2\xee\xe1\xf1\xa1\ -\xd7\x4e\x2f\xd3\xe1\x10\xd8\x30\x34\xdd\xa3\xce\x89\xd7\xce\xae\ -\x1d\xcf\x76\xce\xd4\x6b\xe7\xc2\xeb\x34\x19\x31\x11\xc8\xd1\x35\ -\x33\x1c\x32\x65\xb4\x51\xa8\x7b\xf7\x23\x08\x50\x94\x13\x9d\xad\ -\xc5\x6b\x67\x84\x2a\x4b\x4e\x27\x32\x29\x6c\xf3\xfd\x9d\x1c\x7f\ -\x76\x43\x19\xc7\x39\x91\x3a\xa6\x3e\x32\x6a\x65\x0b\x10\x49\xd4\ -\x07\xd5\x3d\xf2\xda\xd9\x55\xaa\xfe\xac\x84\x0a\x8b\x88\xaa\x21\ -\x13\x73\x1c\x4e\x1a\x39\x30\x03\x51\x61\xc9\xd9\x67\xf9\x51\xc9\ -\x24\x46\x9d\xf3\xa7\x39\xcc\xef\xd3\xe9\x15\xe1\xa6\x30\x56\x8d\ -\xbd\xec\x33\x27\xbd\x1a\xa3\x55\x75\x6a\x34\x5d\x26\x0c\xbd\xd6\ -\x30\x9f\xf2\x74\xf4\x67\xa7\x90\x5b\x2c\xa8\x86\xd1\xa7\x0a\xa3\ -\x50\x2a\xf6\x5b\x0a\x53\xc3\x6a\x9e\x59\xd5\x44\x9f\x69\x1f\x78\ -\xce\x89\xdb\x9b\xd2\xcf\x6b\x6c\x04\x63\x53\x9a\x30\xb5\xd3\x39\ -\x0c\x68\xc2\x91\xb5\xe4\x52\x91\x01\xfe\x1b\x51\xce\xe7\x2d\x55\ -\x6f\xae\x74\x30\xd5\x6d\x46\xf9\x76\x59\xfb\xca\x62\xac\xc3\x81\ -\xaa\xd8\xa1\xe7\x86\x1b\x97\x81\x73\x01\xa7\x1a\xc4\x8d\xb9\xd5\ -\x00\xba\x5a\xf7\x9b\x39\x3d\xfd\x34\xe5\xe7\xb5\xdd\xe0\x5d\x0b\ -\xa8\xc6\x03\xfb\x0d\x9f\x98\xc0\x27\xa5\x4d\x80\xe1\x76\xd6\xda\ -\x9b\x37\x1d\xce\x28\x8d\xe4\x68\x70\xb0\x57\x02\x83\x29\x35\x03\ -\x82\xce\x5e\x09\x13\x0a\xb5\xe6\x19\x2e\xb0\x74\x6a\xb8\x25\x2c\ -\x5d\x76\x1b\x07\x8b\x57\x0a\x06\xef\xed\xb3\x7e\x97\x18\x83\x66\ -\xcc\x83\xcc\xd2\x62\xa4\x39\x3f\xe8\xa7\xb4\x46\x8f\x92\x92\x5f\ -\xb3\xb8\xde\xa9\xae\x43\xa6\x09\xfe\x35\x21\x90\xa0\xe2\x60\x02\ -\x46\xe4\x07\x3a\x19\x91\xfd\x5f\x08\x8a\xf7\xf7\xb5\x8a\x12\x8e\ -\xe7\x9c\x0a\x6e\x6c\xce\xfe\x0a\x82\x6e\xe7\xf0\xd0\x82\x70\x30\ -\x47\x1a\x2a\x00\xd1\xdd\x3f\xc1\x47\x93\x5e\x96\xc9\x7d\x9e\x40\ -\x77\xff\x18\xa9\xee\xaa\xfc\xd8\x2a\xa2\xee\xa7\xb5\x35\xf3\x07\ -\x61\xd3\xcd\x42\x0f\xf3\x33\x8b\x58\x33\xb9\xa7\x85\xc2\x1e\x6a\ -\x24\x2b\xee\x8a\x9a\xf0\x6e\x69\x5f\x64\xd0\xce\xf2\xec\xba\xa2\ -\xdf\x6b\xa7\x48\x38\x85\xc9\x12\x79\x55\xd0\x5c\x09\x32\xd7\x06\ -\x98\x48\x48\x7c\x93\x28\x78\x3c\xd4\xbc\x03\xfe\x77\xb8\xb9\x4d\ -\xdc\xbc\x2b\x17\xaf\x86\x9c\xbd\xdc\xdb\xb6\x0b\x9f\xfb\x87\x7b\ -\x0d\xf0\x79\x74\xd2\x04\x9f\xaf\x8f\x1e\x09\x3e\xa7\xb6\xda\x61\ -\xe8\xe2\xc2\xf3\x68\xb5\xc2\xf3\x70\x7d\x85\xa7\xeb\x7d\x1e\x11\ -\x44\x0f\x76\x20\xba\xd8\xd6\xdb\x06\xd1\x57\x8d\x0f\x63\x19\xe8\ -\x3a\x3c\x69\x40\xae\x57\x9d\x26\xe4\x3a\x78\x2c\xe4\xfa\xe1\x62\ -\x61\x07\x5b\x8b\x61\x6b\x7f\xef\xfe\xb8\x55\x38\x5f\x08\xfe\xcd\ -\xec\xde\xc2\xd4\xf3\x2c\xa1\x2f\xc7\x3f\x5f\x3f\x3c\x77\x5f\x0e\ -\x30\x6d\xa7\xe9\xdb\xb1\x83\xe0\x39\x19\x85\x20\x48\x87\x68\x16\ -\x31\x4e\xb3\xde\x9a\x50\x4c\xde\x7d\xc0\x9f\x20\xf8\x09\x81\x39\ -\xdc\xce\xc4\x8c\xcf\x14\x49\x44\x00\x8a\x4f\x2c\x4d\xdf\x80\x41\ -\x3e\x1a\x71\x11\x7b\xa5\x3e\x90\x5f\x92\x09\x08\x08\xa2\x83\x24\ -\x52\xc0\x73\x42\x45\xe0\x0a\x05\x33\x92\x19\xe7\x6c\xaa\x2f\x23\ -\xb0\x53\x1e\x5e\x1a\x2c\xc6\xed\xbf\x50\x8b\x4c\x5a\x9f\x6a\x98\ -\x51\xd3\x2e\x35\x96\x5a\xb3\x3e\x5f\x41\xf2\x12\x41\x62\x0d\x62\ -\x3b\xa4\x5e\xa6\xc2\x93\x8c\x94\x32\xb1\xc4\x6b\x66\xde\x03\xf6\ -\xd3\x8a\x32\xf5\x7e\x3b\x6a\x97\x17\xef\x09\x8b\x62\xa9\xcc\xba\ -\xb7\xd1\xd6\xb7\x89\x76\xb2\x11\x4c\xb8\xa3\xc4\x69\xc2\x04\xc7\ -\x3c\x85\x05\x8c\xda\x80\xe9\x18\x17\x85\x95\x7d\x3f\x19\x92\x08\ -\xb4\xa6\x43\x17\x39\x8c\x43\x66\x5b\x1b\xf9\xd6\xd2\x03\x1c\x5a\ -\xa1\xb4\x5f\x1c\xbf\xbd\x50\x8e\xe6\x14\xd8\x4a\xb0\xb2\x81\x7f\ -\x6e\xa5\x3e\xc9\x20\x5d\xdd\x73\x0f\x37\xe1\xb9\xeb\xcd\x65\x53\ -\xb7\x4c\xd3\x0c\xe5\x23\x3a\xd1\x36\x31\xa5\x9e\x8b\xa9\x69\xc4\ -\x4c\xe8\x32\x12\xe6\x28\x50\x82\x72\x12\x4f\x4c\x28\x31\x2d\x50\ -\xa5\x41\x3d\x27\x70\x8b\x39\x82\x0d\xc8\xe5\xc0\xff\x1a\x83\xe8\ -\x85\x80\x8c\xac\x18\xa1\xd1\x14\x1c\x82\x4d\x78\xfc\x85\x54\x3e\ -\x94\x35\xd9\x8a\xc7\x0f\xac\x5c\x5c\xe9\x95\x93\x7c\x95\x09\xde\ -\x79\x7f\x8d\xf7\xbf\xda\x08\x6e\xaf\xb0\x0f\xf3\x5e\x01\x35\xe8\ -\xe9\x2e\x59\x6a\xd7\x9e\x02\xf5\xc3\xf4\x75\x52\xba\xf1\x42\xcc\ -\x24\xde\x48\x85\xe5\x12\x36\x96\x86\x18\x55\x02\x3b\x4e\x0c\xb1\ -\x89\x93\xb5\x1d\x9c\xf6\xdd\xca\xd1\x6d\x9d\x1a\x4f\xb3\xa6\x5a\ -\xdd\x61\x0f\x36\xe2\xb0\xcd\x3d\xef\x62\x8f\xb9\x74\x00\x4c\x06\ -\x89\x12\xcc\x6d\x0d\xfe\xe1\x53\x41\x22\x7a\x03\x0e\x8d\x23\x19\ -\x00\x27\x21\xd0\xdb\xc9\x9f\xdb\xc9\xf5\x4e\x1f\xf4\xa1\x8b\x5c\ -\xa3\x9d\x1b\xd5\x37\xb1\xfb\x1b\xf1\xa3\xa3\xf5\x26\x7e\xfc\xb5\ -\xb0\xaf\xcf\xa7\x9d\xe8\x34\xe1\x53\x4d\x74\xd2\x37\x8a\xfa\x76\ -\x5b\x4c\xe3\x54\xf4\x37\x35\x62\xd8\x17\xda\x16\x91\xe8\x90\xc6\ -\x45\xbd\x80\xc8\x12\x4c\x48\x48\x6f\x21\xeb\x76\xa7\x8c\x73\x26\ -\x9b\x29\x00\x7a\x80\x59\x1f\x41\x6d\x2a\x6f\x5b\x05\x6f\x2e\xf8\ -\xeb\x54\xee\x2e\x0a\xea\xa2\x60\x89\xb3\x0f\x4b\x44\xc1\xf1\x9a\ -\xa3\x20\xcd\xfa\xf6\xd7\x45\x24\x58\x81\x6c\x3c\x5b\xfa\xda\x2a\ -\xf9\xf2\x9c\xa4\x47\x5f\x36\xe1\xcd\x57\x4e\xe4\x54\x0f\x14\xbb\ -\x06\x71\xcb\xf9\x75\xaa\xc2\xdf\x4e\xe6\xce\xa9\x17\x38\xf5\x12\ -\x35\x6d\xdd\x7b\x95\xe6\x97\x11\x8b\xdd\xe3\xc3\xd8\xe7\x49\x00\ -\x84\x33\x6d\x4e\xc9\x26\x5f\xa6\x2c\x08\xcc\xcf\x4c\xc0\x87\x80\ -\x99\x4a\x60\x72\x24\x80\x25\x3c\x38\x2c\xdf\xda\xaa\x38\xa2\x2f\ -\x74\x86\xab\x81\x5b\x25\x91\xae\xa1\x24\x58\x2b\x33\xc3\xc0\xed\ -\xb4\x40\x66\x85\x81\x92\x51\xfd\xae\xd5\x62\x53\xdc\xdf\xd8\xed\ -\x07\xb8\x3c\xa7\x3e\x84\x92\x07\xa0\xae\x17\xd7\x73\x98\x3a\xec\ -\xeb\x96\xe7\x78\xf1\x2e\x61\xdc\x1e\x9f\xfc\xc0\x21\xc2\x15\x5e\ -\x29\x39\x9e\xd8\xf1\x0b\x2e\x47\xd7\xa0\x22\x26\xec\xdb\xa6\x2d\ -\xe5\xb3\x1b\x16\x3f\xc9\x70\xdf\xdc\x26\xeb\x14\x05\xee\xb7\xc9\ -\xda\x39\x27\x0a\xec\x3b\x00\xbc\x59\xfb\x69\xc5\x02\x91\x56\x46\ -\xb7\xe3\x8d\x64\xec\x3b\x30\xaf\x09\x1a\x66\xb6\x35\x99\xc0\x76\ -\x3d\x72\x2f\x6a\x49\x90\x58\x2a\x69\x30\xeb\x12\xfe\x7b\xc7\xa6\ -\x6a\x2a\x01\xd4\x63\xec\xae\x5a\x05\xbe\xff\xf3\x65\xb7\xc3\xba\ -\xd8\x65\x5f\xaf\x96\x8f\x1f\xda\x9b\xbf\x47\x2d\x29\x76\x3e\x9a\ -\xd8\xfc\x67\xcb\x4a\xff\x46\xa0\x6b\x10\x65\x3d\xf4\xf1\x0e\x3b\ -\x34\xaf\x67\x77\xd8\x61\x2d\x87\x1d\xce\x65\xd2\xe7\xd0\x8b\x99\ -\xa8\xc3\xbd\xc0\x51\x35\x52\x57\xda\xab\xef\xc5\xe0\xb3\x81\x2d\ -\x7c\x2c\x0a\x45\x54\x4c\x88\x61\xb6\x39\xb0\xed\xc8\x2d\x83\x91\ -\xab\x93\x4a\xbe\x17\x25\x58\x36\x61\xfb\x42\xe3\x98\x33\x24\x63\ -\xc5\xe4\xa3\x9f\xda\x59\x2b\xe0\xd5\x2d\xe5\x09\xcc\xe9\x98\xae\ -\xb1\xdb\x79\xb9\x57\xfe\xe3\xb5\x33\xca\x26\x51\x11\x63\xcf\x56\ -\xa3\xd7\xf9\xba\x9f\x24\x32\x2e\x5f\xbb\x94\x80\x21\xaf\x11\xca\ -\xc7\xef\xef\x83\x07\x05\x14\x7c\xcf\x78\x94\x80\xa0\x5a\xaf\x2c\ -\x11\xfe\xe5\xc8\xcf\x82\xbe\x53\x09\xfa\x3c\xde\x0f\x2a\xf1\x5e\ -\x0a\xf5\x79\x55\x4a\x01\x5e\x18\x69\xc6\x92\x33\x66\xcc\x52\x4d\ -\x7e\x1a\x33\xfb\x5c\xe6\xac\x75\xd4\x22\xe9\x77\x2f\x67\xad\xfd\ -\xfd\x96\x6d\x05\xbc\x98\x8d\x23\x1a\x0f\x12\xe1\xb6\xc3\xba\xff\ -\x5d\xb9\xfb\x0b\xec\x41\xbe\x60\x40\xf6\x64\xa2\x7c\x40\x5d\xca\ -\xb3\xec\xa7\x4f\x18\x8e\x32\x4a\x25\x6a\xa7\xc9\xec\x48\xaa\xe5\ -\xcc\xe7\x51\x33\x47\xa8\x8a\x4f\xa2\xec\xf3\x18\x1b\x2c\x41\x74\ -\xf7\xdb\x55\xa2\xc3\x9c\x9e\x0f\x3e\x4b\xcd\x45\xb1\x44\xb1\x5c\ -\xda\xe9\x27\x53\xfa\x65\x68\x2d\xe7\x46\x9d\x05\xe6\x05\x37\x6b\ -\x32\x77\xa0\xab\x56\x9b\xaa\xca\x8b\x54\xb2\xdc\xd6\xa7\x56\x56\ -\x6d\xd6\x5b\xa8\xa0\x6e\x45\x99\xbc\x27\xae\x57\xa6\xa0\x6e\x45\ -\x99\x52\x52\xaa\xd7\x68\x6e\xca\xea\x6a\x95\x07\xdc\x27\x7c\x0a\ -\xb4\x0b\x08\xed\x42\x27\x3b\x09\x65\x77\x91\xf1\xde\x6b\x27\xac\ -\xfb\xec\x7f\x23\x8e\x6e\x06\ +\x00\x3d\x0e\x78\x9c\xed\x5b\x6d\x6f\xdb\x36\x10\xfe\xde\x5f\x41\ +\xf8\xd3\x06\x74\xb5\x9d\xb7\x26\x81\xe2\x61\x6d\x9a\x36\x43\xbb\ +\xa5\x73\xd6\x7e\x1c\x68\xe9\x6c\x71\xa1\x48\x8d\xa4\x62\xbb\xbf\ +\x7e\x47\x4a\xb2\x2c\x5b\x52\xe2\xf8\x25\x40\xe0\xa2\x45\x25\x1e\ +\x7d\x77\x3c\xdd\x3d\x77\x47\x4a\xde\xaf\x93\x88\x93\x7b\x50\x9a\ +\x49\x71\xd1\xea\xbe\xe9\xb4\x08\x08\x5f\x06\x4c\x8c\x2e\x5a\x7f\ +\xdf\x5e\xfd\x72\xda\xfa\xb5\xf7\xca\x4b\x58\x31\xe9\x08\x27\xf5\ +\x5e\x11\xcf\xe7\x54\xeb\xde\xc7\x84\x9d\x9f\x5f\x32\xca\xe5\x08\ +\xff\xe7\xa3\x3e\x18\x83\x3f\xd6\xbf\x29\x3f\xf4\xda\xe9\x1c\x9c\ +\x3c\x66\xc1\x08\x0c\x71\xf7\x17\xad\xaf\xdf\xdd\x6d\x8b\x08\x1a\ +\xc1\x45\xab\x89\x87\x15\x45\xbc\x58\xc9\x18\x94\x99\x66\x3f\x18\ +\x81\x8c\xc0\xa8\xa9\x23\x12\x4f\x81\x6f\xdc\x15\xf1\x26\xbd\x8e\ +\xd7\x9e\x64\x37\x53\x7b\x33\xcd\x6e\x50\x03\x13\xf6\x8e\xdf\x1e\ +\x7b\xed\xf4\x32\x1d\x0e\x81\x8d\x42\xd3\x3b\x39\x38\xf3\xda\xd9\ +\xb5\xe3\xd9\xce\x99\x7a\xed\x5c\x78\x95\x26\x63\x26\x02\x39\xbe\ +\x65\x86\x43\xa6\x8c\x36\x0a\x75\xef\x7d\x04\x01\x8a\x72\xa2\xb3\ +\xb5\x78\xed\x8c\xb0\xcc\x92\xd3\xa9\x4c\x0a\xdb\x7c\x7b\x27\x27\ +\x9f\xdd\x50\xc6\x71\x41\xa4\x8e\xa9\x8f\x8c\x5a\xd9\x02\x44\x12\ +\x0d\x40\xf5\x4e\xbc\x76\x76\x95\xaa\x3f\x2f\x61\x89\x45\x44\xd5\ +\x88\x89\x05\x0e\x67\x8d\x1c\x98\x81\xa8\xb0\xe4\xfc\xb3\xfc\xa8\ +\x64\x12\xa3\xce\xf9\xd3\x1c\xe5\xf7\xe9\xf4\x25\xe1\xa6\x30\x56\ +\x85\xbd\xec\x33\x27\xfd\x0a\xa3\x2d\xeb\xd4\x68\xba\x4c\x18\x7a\ +\xad\x61\x3e\xe5\xe9\xe8\x3f\x07\x85\xdc\x62\x41\x15\x8c\x3e\x2d\ +\x31\x0a\xa5\x62\x3f\xa4\x30\x15\xac\x16\x99\x2d\x9b\xe8\x33\x1d\ +\x00\xcf\x39\x71\x7b\x53\xfa\x79\x85\x8d\x60\x62\x4a\x13\x66\x76\ +\xba\x84\x21\x4d\x38\xb2\x96\x5c\x2a\x32\xc4\x7f\x63\xca\xf9\xa2\ +\xa5\xaa\xcd\x95\x0e\xa6\xba\xcd\x29\xdf\x2e\x6b\xbf\xb4\x18\xeb\ +\x70\xa0\x96\xec\xd0\x77\xc3\x8d\xcb\xc0\xb9\x80\x53\x0d\xe2\xc6\ +\xc2\x6a\x00\x5d\xad\xf7\xd5\x9c\x9f\x7f\x9a\xf1\xf3\xda\x6e\xf0\ +\xa1\x05\x2c\xc7\x03\xfb\x01\x9f\x98\xc0\x27\xa5\x4d\x80\xe1\x76\ +\xd1\xea\x2c\x9a\x0e\x67\x94\x46\x72\x34\x38\xea\x94\xc0\x60\x46\ +\xcd\x80\xe0\xa0\x53\xc2\x84\x42\xad\x45\x86\x35\x96\x4e\x0d\xb7\ +\x82\xa5\xcb\x6e\xe3\x60\xf1\x46\xc1\xf0\xbd\x7d\xd6\xef\x12\x63\ +\xd0\x8c\x79\x90\x59\x5a\x8c\x34\xe7\x07\x83\x94\xd6\xe8\x51\x52\ +\xf2\x5b\x16\x57\x3b\xd5\x6d\xc8\x34\xc1\xbf\x26\x04\x12\x2c\x39\ +\x98\x80\x31\xf9\x8e\x4e\x46\xe4\xe0\x5f\x04\xc5\xc7\xfb\xda\x92\ +\x12\x8e\xe7\x82\x0a\x6e\x6c\xc1\xfe\x0a\x82\xde\x41\xf7\xc8\x82\ +\x70\xb0\x40\x1a\x29\x00\x91\x12\xd3\xcb\x32\x79\xc0\x13\x48\xa9\ +\xee\xaa\xfc\xd8\x96\x44\x3d\x4e\x6b\x6b\xe6\x0f\xc2\xa6\x9b\x5a\ +\x0f\xf3\x33\x8b\x58\x33\xb9\xa7\x85\xc2\x9e\x6a\x24\x2b\xee\x86\ +\x9a\xf0\x61\x69\x5f\x64\xd0\xce\xf2\xec\xa6\xa2\xdf\x6b\xa7\x48\ +\x38\x83\xc9\x12\x79\x5d\xd0\x5c\x0b\x32\x37\x06\x98\x48\x48\x7c\ +\x93\x28\x78\x3e\xd4\x7c\x00\xfe\xf7\xb8\xb9\x4b\xdc\x7c\x28\x17\ +\xaf\x87\x9c\xfd\xdc\xdb\x76\x0b\x9f\xdd\xe3\x4e\x3d\x7c\x76\x4f\ +\xce\x1a\xe0\xb3\x7b\x7a\xf2\x4c\xf0\x39\xb3\xd5\x1e\x43\xeb\x0b\ +\xcf\x93\xf5\x0a\xcf\xe3\xcd\x15\x9e\xae\xf7\x79\x46\x10\x3d\xda\ +\x83\x68\xbd\xad\x77\x0d\xa2\x87\x8d\x0f\x63\x15\xe8\x3a\x3c\xac\ +\x47\xae\xa3\xe3\x06\xe0\x3a\x79\x2e\xdc\xfa\xee\x22\x61\x0f\x5a\ +\xf5\xa0\xd5\x3d\x5a\x0f\xb5\x4e\x36\x8c\x5a\x64\x64\x65\x3d\x1f\ +\x74\x35\xa3\xf0\x1e\xba\x76\x0a\x5d\xcd\x79\x64\x15\xe8\x3a\x6b\ +\x80\xae\xee\xe9\x61\x53\xcf\xda\x39\x7c\x56\xf0\xfa\x68\x6d\xb7\ +\x47\xb0\x06\x04\xeb\x3c\x1e\xc1\x0a\x0f\x0c\xc1\xbf\x9b\xdf\x1b\ +\x9d\xb9\x9f\x25\x0c\xe4\xe4\x9f\xd3\xa7\xf7\x1e\xd7\x43\x6c\x3b\ +\xd2\xf6\xc3\xb1\x83\xe0\x35\x19\x87\x20\xc8\x01\xd1\x2c\x62\x9c\ +\x66\x7b\x83\x84\x62\xf3\x31\x00\xfc\x09\xc2\xa0\x10\xd8\x83\xd8\ +\x99\xd8\xb1\x30\x45\x12\x11\x80\xe2\x53\x4b\xd3\x77\x60\x90\x8f\ +\x46\x84\xe4\x1c\xe7\x93\x7f\x25\x13\x10\x10\x84\x08\x49\xa4\x80\ +\xd7\x84\x8a\xc0\x35\x3a\x66\x2c\x33\xce\xd9\x54\x5f\x46\x60\xa7\ +\x3c\xbd\xb5\xa9\x47\xf0\xdf\x51\x8b\x4c\xda\x80\x6a\x98\x53\xd3\ +\x2e\x35\x96\x5a\xb3\x01\x5f\x43\xf2\x0a\x91\x62\x0d\x62\x77\x78\ +\xfa\x99\x0a\x2f\x32\x52\xca\xc4\x12\xaf\xb9\x79\x4f\x38\x0f\x28\ +\xda\xec\xc7\x9d\x08\x5c\x5f\xbd\x27\x2c\x8a\xa5\x32\x9b\x3e\x06\ +\xd8\xdc\x21\xc0\xd9\x56\x30\xe1\x81\x16\xad\x09\x13\x1c\xf3\x14\ +\x16\x30\x6a\x03\xa6\x63\x5c\x14\x09\x60\x90\x8c\x48\x04\x5a\xd3\ +\x91\x8b\x1c\xc6\x21\xb3\xad\x8d\x7c\x6b\xe9\x21\x0e\xad\xb1\x35\ +\x51\x1f\xbf\xfd\x10\xab\xad\xb2\x02\x3b\x09\x56\x36\xf4\x2f\xad\ +\xd4\x17\x19\xa4\xeb\x7b\xee\xf1\x36\x3c\x77\xb3\xb9\x6c\xe6\x96\ +\x69\x9a\xa1\x7c\x4c\xa7\xda\x26\xa6\xd4\x73\x31\x35\x8d\x99\x09\ +\x5d\x46\xc2\x1c\x05\x4a\x50\x4e\xe2\xa9\x09\x25\xa6\x05\xaa\x34\ +\xa8\xd7\x04\xee\x31\x47\xb0\x21\xb9\x1e\xfa\x7f\xc6\x20\xfa\x21\ +\x20\x23\x2b\x46\x68\x34\x05\x87\x60\x1b\x1e\x7f\x25\x95\x0f\x65\ +\x4d\x76\xe2\xf1\x43\x2b\x17\x57\x7a\xe3\x24\xdf\x64\x82\xf7\xde\ +\x5f\xe1\xfd\x87\x5b\xc1\xed\x35\xf6\x91\xdf\x2b\xa0\x06\x3d\xdd\ +\x25\x4b\xed\x1a\x55\xa0\x7e\x98\x1e\x87\xa7\x1b\xc7\xc4\x4c\xe3\ +\xad\x54\x58\x2e\x61\x63\x69\x88\x51\x25\xb0\xed\xc4\x10\x9b\x3a\ +\x59\xbb\xc1\x69\xdf\xad\x1c\xdd\xd6\xa9\xf1\x32\x6b\xaa\xf5\x1d\ +\x76\x85\xed\x93\x15\x1c\xb6\x79\xcf\xae\xde\x63\xae\x1d\x00\x93\ +\x61\xa2\x04\x73\x47\x1b\x3f\xf9\x54\x90\x88\xde\x81\x43\xe3\x48\ +\x06\xc0\x49\x08\xf4\x7e\xfa\xf3\x6e\x72\xbd\xd3\x07\x7d\xe8\x2a\ +\xd7\x68\xef\x46\xd5\x4d\x6c\x77\x2b\x7e\xf4\xc0\xee\xdc\xaa\x89\ +\x1f\x7f\x2d\xec\xeb\x3f\xb3\x4e\x74\x96\xf0\xa9\x26\x3a\x19\x18\ +\x45\x7d\xbb\x37\xa6\x71\x2a\xfa\x9b\x1a\x33\xec\x0b\x6d\x8b\x48\ +\x74\x48\xe3\xa2\x5e\x40\x64\x09\xa6\x24\xa4\xf7\x90\x75\xbb\x33\ +\xc6\x39\x93\xed\x14\x00\x7d\xc0\xac\x8f\xa0\x36\x93\xb7\xab\x82\ +\x37\x17\xfc\xe7\x4c\xee\x3e\x0a\xaa\xa2\x60\x85\x77\xb7\x56\x88\ +\x82\xb7\x1b\x8e\x82\x34\xeb\xdb\x5f\x17\x91\x60\x05\xb2\xc9\x7c\ +\xe9\x6b\xab\xe4\xeb\x4b\x92\xbe\xba\xb7\x0d\x6f\xbe\x71\x22\x67\ +\x7a\xa0\xd8\x0d\x88\x5b\xcd\xaf\x53\x15\xfe\x70\x32\xf7\x4e\x5d\ +\xe3\xd4\x2b\xd4\xb4\x55\x27\x2c\xcd\x9b\xe0\xf5\xee\xf1\x61\xe2\ +\xf3\x24\x00\xc2\x99\x36\xe7\x64\x9b\x27\x2a\x35\x81\xf9\x99\x09\ +\xf8\x10\x30\xb3\x14\x98\x1c\x09\x60\x09\x4f\x0e\xcb\xdf\x6c\x55\ +\x1c\xd1\x5f\x74\x86\xab\x81\x5b\x25\x91\xae\xa1\x24\x58\x2b\x33\ +\xc3\xc0\xed\xb4\x40\x66\x85\xa1\x92\x51\xf5\xae\x55\xbd\x29\x1e\ +\x6f\xec\xf6\x13\x5c\x9e\x53\x1f\x42\xc9\x03\x50\xb7\xf5\xf5\x1c\ +\xa6\x0e\x7b\xe6\xf2\x1a\x2f\xde\x25\x8c\xdb\xd7\xbf\x3f\x70\x88\ +\x70\x85\x37\x4a\x4e\xa6\x76\xfc\x8a\xcb\xf1\x2d\xa8\x88\x09\x7b\ +\xe4\xb4\xa3\x7c\x76\xc7\xe2\x17\x19\xee\xdb\xdb\x64\x9d\xa1\xc0\ +\xe3\x36\x59\x0f\x2e\x89\x02\x7b\x06\x80\x37\x1b\x7f\xdb\xba\x40\ +\xa4\xb5\xd1\xed\xed\x56\x32\xf6\x03\x98\xd7\x04\x0d\x73\xdb\x9a\ +\x4c\x60\xbb\x1e\xb9\xd3\x5a\x12\x24\x96\x4a\x1a\xcc\xba\x82\xff\ +\x3e\xb0\xa9\x9a\x4a\x00\xf5\x1c\xbb\xab\x56\x81\x6f\x7f\x7d\xd9\ +\xef\xb0\xd6\xbb\xec\xe9\x7a\xf9\xf8\xa9\xbd\xf9\x7b\xd4\x92\x62\ +\xe7\xa3\x89\xcd\x7f\xb6\xac\xf4\xef\x04\xba\x06\x51\xd6\x43\x9f\ +\xef\x8d\x87\xe6\xf5\xec\xdf\x78\xd8\xc8\x1b\x0f\x97\x32\x19\x70\ +\xe8\xc7\x4c\x54\xe1\x5e\xe0\xa8\x1a\xa9\x6b\xed\xd5\xf7\x63\xf0\ +\xd9\xd0\x16\x3e\x16\x85\x22\x2a\xa6\xc4\x30\xdb\x1c\xd8\x76\xe4\ +\x9e\xc1\xd8\xd5\x49\x25\xdf\x8b\x12\x2c\x9b\xb0\x7d\xa1\x71\xcc\ +\x19\x92\xb1\x62\xf2\xd1\x4f\xed\xac\x35\xf0\xea\x9e\xf2\x04\x16\ +\x74\x4c\xd7\xd8\x3b\x78\xd3\x29\xff\xf1\xda\x19\x65\x9b\xa8\x88\ +\xb1\x67\xab\xd1\xdb\x7c\xdd\x2f\x12\x19\x57\xaf\x5d\x4a\xc0\x90\ +\xd7\x08\xe5\xcf\x87\x1e\x83\x07\x05\x14\x7c\xcb\x78\x94\x80\x60\ +\xb9\x5e\x59\x21\xfc\xcb\x91\x9f\x05\xfd\xc1\x52\xd0\xe7\xf1\x7e\ +\xb4\x14\xef\xa5\x50\x5f\x54\xa5\x14\xe0\x85\x91\xe6\x2c\x39\x67\ +\xc6\x2c\xd5\xe4\x6f\x93\x67\x9f\xfb\x5d\xb4\x4e\x5a\x24\xfd\x6e\ +\xef\xa2\xd5\xed\xb6\x6c\x2b\xe0\xc5\x6c\x12\xd1\x78\x98\x08\xb7\ +\x1d\xd6\xfb\xef\xc6\xdd\x5f\x61\x0f\xf2\x05\x03\xb2\x2f\x13\xe5\ +\x03\xea\x52\x9e\x65\x3f\xdd\xc4\x70\x94\x51\x2a\x51\x3b\x4d\xe6\ +\x47\x52\x2d\xe7\x3e\xef\x9c\x7b\x8f\xaa\xf8\xa4\xd3\x3e\x8f\x89\ +\xc1\x12\x44\xf7\xbe\xde\x24\x3a\xcc\xe9\xf9\xe0\xab\xd4\x5c\x14\ +\x4b\x14\xcb\xa5\x9d\x7e\xf2\xa9\xdf\x84\xd6\x72\x6e\xd4\x59\x60\ +\x51\x70\xb3\x26\x0b\x6f\x75\x55\x6a\xb3\xac\x72\x9d\x4a\x96\xdb\ +\xe6\xd4\xca\xaa\xcd\x6a\x0b\x15\xd4\x9d\x28\x93\xf7\xc4\xd5\xca\ +\x14\xd4\x9d\x28\x53\x4a\x4a\xd5\x1a\x2d\x4c\x59\x5f\xad\xf2\x80\ +\xfb\x04\x59\x81\x76\x01\xa1\x5d\xe8\x64\x6f\x42\xd9\x5d\x64\xbc\ +\xf7\xda\x09\xeb\xbd\xfa\x1f\x60\x53\xb8\xc3\ \x00\x00\x07\x4c\ \x00\ \x00\x29\xd1\x78\x9c\xed\x59\x5b\x8f\xe2\x46\x16\x7e\xef\x5f\xe1\ @@ -30235,6 +30238,115 @@ qt_resource_data = "\ \xd2\x9d\x05\x58\x30\x1c\x10\x5c\xc1\xc0\xb9\x84\x28\x3a\x0e\x7c\ \x1e\x69\x22\x66\xf0\xbf\x6e\xdf\x34\x9b\xcb\xfa\xdb\x5d\xc5\xd7\ \x0f\x77\x73\x77\xf7\x79\xb8\xfb\x37\x54\x09\xba\x52\ +\x00\x00\x06\xab\ +\x00\ +\x00\x22\x6d\x78\x9c\xed\x58\xeb\x8f\xdb\x36\x12\xff\xbe\x7f\x85\ +\x4e\xf9\x92\x45\x4d\x8a\x0f\x49\x14\xb5\xf6\x16\xc5\x05\x29\x0a\ +\xb4\x28\x70\x4d\x70\x1f\x0f\xb2\x44\xdb\xca\xca\x92\x41\xc9\x6b\ +\x3b\x7f\x7d\x87\xb2\x5e\xde\xd5\x3e\x2f\x71\xda\xbb\x18\x58\xac\ +\x34\x0f\xce\x70\xe6\x37\x43\x6a\xa6\x3f\xee\xd7\x99\x75\xab\x74\ +\x99\x16\xf9\xcc\xa6\x98\xd8\x96\xca\xe3\x22\x49\xf3\xe5\xcc\xfe\ +\xf8\xe1\x3d\x0a\x6c\xab\xac\xa2\x3c\x89\xb2\x22\x57\x33\x3b\x2f\ +\xec\x1f\xaf\x2f\xa6\xff\x40\xc8\xfa\xa7\x56\x51\xa5\x12\x6b\x97\ +\x56\x2b\xeb\x97\xfc\xa6\x8c\xa3\x8d\xb2\xde\xae\xaa\x6a\x13\x3a\ +\xce\x6e\xb7\xc3\x69\x43\xc4\x85\x5e\x3a\x97\x16\x42\xd7\x17\x17\ +\xd3\xf2\x76\x79\x61\x59\x16\xd8\xcd\xcb\x30\x89\x67\x76\xa3\xb0\ +\xd9\xea\xac\x16\x4c\x62\x47\x65\x6a\xad\xf2\xaa\x74\x28\xa6\x8e\ +\xdd\x8b\xc7\xbd\x78\x6c\xac\xa7\xb7\x2a\x2e\xd6\xeb\x22\x2f\x6b\ +\xcd\xbc\x7c\x33\x10\xd6\xc9\xa2\x93\x36\xde\xec\x78\x2d\x44\xa5\ +\x94\x0e\x61\x0e\x63\x08\x24\x50\x79\xc8\xab\x68\x8f\x4e\x55\xc1\ +\xc7\x31\x55\x46\x08\x71\x80\xd7\x4b\x3e\x4f\x2a\xdc\x67\x10\x8a\ +\x07\x9d\xa9\xb9\x43\xeb\x10\xfe\x0d\xfc\x75\x0a\x2d\x01\x97\xc5\ +\x56\xc7\x6a\x01\x9a\x0a\xe7\xaa\x72\xde\x7d\x78\xd7\x31\x11\xc1\ +\x49\x95\x0c\x96\x69\xa3\x7f\x62\xf7\x24\x25\x79\xb4\x56\xe5\x26\ +\x8a\x55\xe9\xb4\xf4\x5a\x7f\x97\x26\xd5\x6a\x66\xfb\xee\x66\x5f\ +\xbf\xaf\x54\xba\x5c\x55\x03\x42\x9a\xcc\x6c\xd8\x21\x93\x81\x57\ +\xbf\x0f\x00\x44\x8f\x02\xcd\x72\x61\xc7\x21\xd8\x0d\xb0\x6b\x69\ +\x29\xb9\xac\x45\x5a\xbf\xc3\xa4\x88\x8d\x23\x33\xfb\x27\x1d\xaf\ +\xfe\xf3\x53\x92\x60\x13\xbc\x6b\x90\x99\x26\x6a\x51\x1a\xd9\xa3\ +\x45\xf3\x06\x26\x45\xcd\x03\x2e\x84\x4d\x45\xfa\x67\x1d\x25\x29\ +\x80\xe5\x28\x77\x94\x3c\xe5\x70\x21\x79\xa3\x03\x5a\x65\x55\x6c\ +\x5a\x59\xf0\xa2\x3a\x64\x60\xda\x10\x51\x5c\x64\x85\x0e\xdf\x40\ +\xfa\x16\x41\x74\x55\x93\x0a\x88\x4e\x5a\x1d\x42\x7a\x65\xf7\x3a\ +\xc5\x62\x51\x2a\x08\x07\x19\xd0\xea\x88\x80\x06\xd8\xf2\x6c\xcb\ +\x79\x89\x35\xdf\x5f\x2c\x9e\x61\x8d\x8e\x5b\x13\x9d\xb5\xa9\x73\ +\xba\xed\xc7\xa3\xd4\x26\x08\xdc\xc8\x54\x0c\xeb\x47\xd9\x2e\x3a\ +\x94\x9d\x91\x1a\x94\xe1\x4a\x2b\x28\xa2\x37\x23\xf1\x7c\x34\xdc\ +\xb2\x5f\x86\x82\xe7\x0c\x13\x2e\x02\xe2\x77\xd4\x03\x50\x3d\x17\ +\x13\x42\x5d\x3a\x90\x65\x40\x65\x38\x08\x98\xef\x06\xbd\x2c\x50\ +\x25\x66\xc2\xa5\x03\xe2\xb2\xb1\xf5\x31\x4f\x2b\x28\xc2\x6d\xa9\ +\xf4\x1f\x06\xc8\xbf\xe7\x1f\x4b\x75\x4f\xea\x83\x8e\xf2\x12\xaa\ +\x66\x3d\xb3\x2b\xf3\x98\x41\xdf\x7a\x8b\xd8\x04\xb1\xcb\x3e\x7a\ +\x5f\x27\x4e\x88\x3d\x11\x29\x14\x7c\xdb\x58\x3d\xb5\xff\xd1\x6a\ +\x82\x5d\x9d\xb1\x9e\x90\x7f\xde\x8a\x42\xe4\x95\x35\x35\x1e\xab\ +\xb3\xf6\x1e\x24\xcf\x1c\x2b\xff\xdb\xf4\x1f\x44\x9f\xaa\x2b\xf2\ +\x77\xe8\x41\x94\x10\x4c\x99\xc7\xe5\x04\x09\x4c\x29\x11\xf2\xe9\ +\x8e\x34\x8e\x32\x7a\x56\x94\xd1\x33\xa3\xcc\x7b\x1d\xca\x46\xf3\ +\xf6\x40\x8a\xc7\xe1\x30\x0a\x9d\xc7\x73\xca\xb1\x24\x3e\xf3\x26\ +\xc8\xc5\x2c\x80\xe4\x5e\xbe\x10\x31\x23\x09\x06\x9f\x5e\x53\x18\ +\x0f\xd5\xd8\x57\x3e\xf3\x1e\xdd\x89\x27\xbf\x64\x01\x05\x01\x16\ +\xbe\x2f\x64\x30\xf1\x09\x96\xd2\x17\x81\x7f\xf9\xb5\xea\xfe\x75\ +\x75\x29\xce\x5a\x97\x67\xbe\x7b\xc2\x3d\xe0\xff\xbd\x2e\x87\xf7\ +\xb7\x27\x2a\x53\xfc\xf7\x95\xf9\x40\xef\x3f\xef\x75\x8c\x9d\x19\ +\x64\xaf\xfc\xc4\xf9\x5f\x02\xd9\x0b\xda\x3f\xfb\x5a\x28\x23\xe7\ +\xbd\xf3\x8b\x33\xa3\x2c\xf8\x8e\x32\xf1\x6c\x94\x91\xe7\x82\x6c\ +\xea\x98\xa9\x4d\xfd\xd4\x0d\x7c\xcc\xb4\x27\xb9\x4d\xd5\xee\xa2\ +\x73\x66\x1e\x75\xde\x6d\xa2\xa5\xaa\x93\x0a\x96\x17\xf5\xaf\x61\ +\xcc\x0b\x9d\x28\xdd\xb2\xfc\xfa\x77\xc2\x6a\xf2\x7e\x1c\x63\x5e\ +\x9c\x7a\x67\x56\xed\xf8\x64\x9c\x5f\xae\xa2\xa4\xd8\xcd\x6c\x76\ +\x97\xf9\xb9\x28\x20\xfe\x1c\xf2\x29\x49\x20\xf8\x5d\x76\xbc\x9f\ +\xd9\x70\x7d\x17\x2c\x08\x3c\x1e\xdc\xe3\x82\x41\x97\x61\xe6\x4a\ +\x9f\x7a\xf7\x98\x5b\xad\x21\xa6\x28\x8b\x0e\x0a\x76\x55\xff\x6b\ +\x11\x5a\xae\x8a\xdd\x52\x9b\xe8\x54\x7a\xab\xee\x6a\x26\x45\xbc\ +\x35\x33\x52\xb4\x3d\x66\xba\x99\xcc\x0d\x24\x8c\x2e\x9a\xcf\x8b\ +\xfd\xf8\x02\xbb\x34\x87\xdd\xa2\x66\xd6\x47\x25\xbb\x17\x93\x46\ +\xa2\x9d\xfe\x51\xe2\x89\x07\x44\xf6\x7d\x21\xdf\x65\x1d\x1e\x66\ +\xad\xa3\x7d\xba\x4e\x3f\xab\xa4\xaf\xca\x4e\xa4\xcc\xa3\x0d\xca\ +\x8b\x44\x95\xe3\xde\x17\xf3\x4f\x00\xb6\x13\x89\x06\x70\x6b\x55\ +\x45\x49\x54\x45\x3d\xb8\x5a\x0a\x93\xb2\xed\x61\x53\x9d\x2c\xc2\ +\x7f\xbd\x7b\xdf\x35\x98\x38\x0e\xff\x5d\xe8\x9b\xbe\x37\x18\x81\ +\x68\x5e\x6c\x61\xe3\x5d\xdf\x33\x13\xc9\x38\x34\xd5\x18\x55\xd7\ +\xe9\x1a\x20\x63\xe6\xbc\x3f\xec\xd7\x19\xc0\xbc\x63\x9c\x08\x57\ +\x87\x8d\xea\x17\x3d\x2e\xab\xd5\x71\x8e\x3b\x3a\xfa\x4e\xe2\x75\ +\x6a\x94\x9c\x3f\xaa\x34\xcb\x7e\x31\x46\x06\x8d\xb0\x59\x34\xad\ +\x32\x75\x5d\xdb\x3c\x3e\xb6\xbb\x70\x9a\x6d\xb4\x7d\x6c\xb0\xcb\ +\xa9\xd3\x86\xa1\x7e\x5b\xf6\xe1\x39\x01\x5d\x17\xe0\x2c\x9a\xab\ +\x6c\x66\xff\x6a\x98\xd6\x3d\xee\x52\x17\xdb\xcd\x1a\x82\xdf\xa8\ +\xb7\x61\xdd\x44\xd5\xaa\x75\xb5\xe9\xd2\x0b\xd8\x46\xf8\x26\x08\ +\xe6\xa2\xee\xcf\xba\xb8\x51\xf5\xf9\xc0\x5d\xaf\x79\x3d\x62\x30\ +\x64\xed\xab\xe9\x37\x60\x25\x9c\x6f\xab\x6a\x48\xfb\x54\xa4\x79\ +\x08\x86\xf3\xa4\xa5\xf6\x9d\xbe\x3f\x77\xe0\x77\x65\x6c\x0e\x98\ +\xf5\xab\xde\x66\x2a\xcc\x8b\xfc\x33\x34\x8a\x56\x1f\x42\xad\x74\ +\x06\x20\xac\x42\xb7\xa5\x25\x11\xf4\x01\xad\xa3\x83\x11\x56\x43\ +\xea\xf1\x08\x09\xc9\xd5\x3a\xd2\x37\x4a\x1f\xf9\xb7\x69\x99\xce\ +\xd3\xcc\x18\xaa\x1f\x33\x75\x95\xa4\xe5\x06\xa2\x12\xa6\xb9\xf1\ +\xfa\xaa\xb8\x55\x7a\x91\x15\xbb\x8e\xaf\xf2\x08\xfe\xa1\x79\x14\ +\xdf\x2c\xeb\xed\x84\x51\x0c\xc5\xbc\x35\xbd\xbd\xeb\xab\x90\x98\ +\xdf\x2c\x86\x3d\x41\x99\x14\xee\x84\x73\xec\x12\xea\xf9\xd4\x62\ +\x12\x73\xca\x84\x9c\x50\x89\x85\xe0\x7e\xe0\x59\x3e\xc5\x82\x52\ +\xca\xc5\x84\x13\x60\xfa\x84\xb8\x16\x17\x70\x34\x70\x38\x1c\x26\ +\xae\xc4\x40\x11\xdc\xb3\x3e\x9f\x1c\x00\x26\x57\x20\x3c\xfa\xbd\ +\x98\x43\x06\xaa\x42\x23\xe8\x4f\xb7\x51\xb5\xd5\x6a\x78\x52\xf7\ +\x8d\x1c\x00\x60\xb0\x0a\x15\x18\x9b\x5f\x7f\x82\x3e\x88\x03\x06\ +\x9f\x68\x0b\xf1\x1d\x07\x2f\xc1\xc1\x7a\x14\x07\xc4\x00\x80\x05\ +\xae\xe0\xd4\xa2\x3e\x76\x3d\x9f\x13\xee\x4f\x24\xf6\x28\x0f\x2c\ +\x1a\x60\xe6\xf9\x0c\xd2\x4f\x2c\x44\x15\xf2\x27\x70\x33\xc4\x9e\ +\x94\xdc\x13\xe3\x38\xe0\x5f\x0e\x07\xcf\x01\x02\xdc\x82\x94\x3b\ +\xff\x0e\x84\x97\x01\x61\xac\xaa\x99\x8b\x5d\x26\xb8\xb9\x0e\xf6\ +\x88\x40\x04\xf2\x2f\x28\x9f\x00\x34\xa4\x20\x00\x0e\x80\x41\xf3\ +\xc8\x26\xd4\xc5\x5c\x4a\xe6\x7b\x96\x19\x3b\x4a\xe9\x73\xc0\x09\ +\x00\xc7\x65\xfe\x38\x38\xbc\x67\x83\x63\x34\xf3\xcf\x46\xd4\x11\ +\x21\x6d\xa2\x08\xf6\x3d\xcf\xf5\x19\xe3\x23\xe9\x7c\x69\x6e\x4f\ +\xb2\xd7\x02\xec\x6c\x89\x83\x1b\xa0\x60\xd0\xb7\x27\x1e\xa6\x84\ +\x71\x8f\x43\xfd\x06\x58\xb8\x01\xdc\x27\x2d\x04\x4f\xd2\xa3\x3e\ +\xd4\xaa\xa1\x72\x57\xf8\x81\xb4\x86\x44\x89\x03\x16\xc8\xc0\x3a\ +\x32\x65\x4d\x43\x2d\x11\xce\x01\x19\xd4\x95\x0e\xc4\x4e\x1d\xdd\ +\x21\xf7\xb6\xda\x25\x4e\x33\xad\x21\x2f\x70\x43\xe2\xc8\x1f\xce\ +\xf4\x47\xea\x77\x24\x15\x70\x7f\x79\x7b\xff\x33\x41\x22\x72\xf9\ +\xec\xec\x7c\xa9\x0e\x30\x56\xc1\xbd\xf9\xbf\x60\x49\xbb\x04\x9b\ +\xb9\xbf\x37\x61\xd8\x17\xd0\xe5\x39\xeb\xa1\xc1\x82\x33\x62\xe3\ +\x68\xec\x09\x70\x8c\x7e\xf8\x3d\xd2\x08\xa6\xce\xf2\xfa\x62\x6a\ +\xae\xc9\xd7\x17\x7f\x02\x3a\x43\xda\x26\ \x00\x00\x07\xc4\ \x00\ \x00\x37\x1e\x78\x9c\xed\x5b\x6d\x8f\x9b\x48\x12\xfe\x3e\xbf\x82\ @@ -33261,6 +33373,10 @@ qt_resource_name = "\ \x0a\xa2\x3b\x27\ \x00\x41\ \x00\x72\x00\x63\x00\x68\x00\x5f\x00\x43\x00\x68\x00\x65\x00\x63\x00\x6b\x00\x2e\x00\x73\x00\x76\x00\x67\ +\x00\x10\ +\x00\xf0\x67\xc7\ +\x00\x41\ +\x00\x72\x00\x63\x00\x68\x00\x5f\x00\x46\x00\x69\x00\x78\x00\x74\x00\x75\x00\x72\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x0d\ \x07\x4a\x92\xc7\ \x00\x41\ @@ -33341,8 +33457,8 @@ qt_resource_name = "\ qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\ -\x00\x00\x00\x10\x00\x02\x00\x00\x00\x01\x00\x00\x00\x3a\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x1f\x00\x00\x00\x1b\ +\x00\x00\x00\x10\x00\x02\x00\x00\x00\x01\x00\x00\x00\x3b\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x20\x00\x00\x00\x1b\ \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x17\x00\x00\x00\x04\ \x00\x00\x01\x2e\x00\x00\x00\x00\x00\x01\x00\x02\x9d\x23\ \x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x06\x6c\x9e\ @@ -33367,37 +33483,38 @@ qt_resource_struct = "\ \x00\x00\x01\x14\x00\x00\x00\x00\x00\x01\x00\x02\x52\xbc\ \x00\x00\x00\xe0\x00\x00\x00\x00\x00\x01\x00\x01\xbc\xaa\ \x00\x00\x02\x3e\x00\x00\x00\x00\x00\x01\x00\x05\x84\xf1\ -\x00\x00\x06\x96\x00\x01\x00\x00\x00\x01\x00\x07\xac\x45\ -\x00\x00\x05\x76\x00\x00\x00\x00\x00\x01\x00\x07\x63\x49\ -\x00\x00\x04\x12\x00\x00\x00\x00\x00\x01\x00\x07\x05\x4d\ -\x00\x00\x07\xc6\x00\x01\x00\x00\x00\x01\x00\x08\x04\x1a\ -\x00\x00\x06\x68\x00\x01\x00\x00\x00\x01\x00\x07\xa5\x70\ -\x00\x00\x04\x4c\x00\x01\x00\x00\x00\x01\x00\x07\x13\xbc\ -\x00\x00\x04\x9e\x00\x01\x00\x00\x00\x01\x00\x07\x27\x43\ -\x00\x00\x03\xb6\x00\x01\x00\x00\x00\x01\x00\x06\xea\xab\ -\x00\x00\x07\x60\x00\x00\x00\x00\x00\x01\x00\x07\xe0\xd6\ -\x00\x00\x05\xb6\x00\x00\x00\x00\x00\x01\x00\x07\x7d\x23\ -\x00\x00\x05\x56\x00\x01\x00\x00\x00\x01\x00\x07\x5b\x81\ -\x00\x00\x03\x94\x00\x01\x00\x00\x00\x01\x00\x06\xe1\xb2\ -\x00\x00\x06\x3c\x00\x01\x00\x00\x00\x01\x00\x07\x9a\x46\ -\x00\x00\x03\x38\x00\x01\x00\x00\x00\x01\x00\x06\xcf\x91\ -\x00\x00\x07\x36\x00\x01\x00\x00\x00\x01\x00\x07\xd9\x41\ -\x00\x00\x04\x7a\x00\x01\x00\x00\x00\x01\x00\x07\x1d\x14\ -\x00\x00\x03\x0e\x00\x01\x00\x00\x00\x01\x00\x06\xc5\xe9\ -\x00\x00\x03\x74\x00\x01\x00\x00\x00\x01\x00\x06\xd9\x44\ -\x00\x00\x05\xe6\x00\x01\x00\x00\x00\x01\x00\x07\x8c\x91\ -\x00\x00\x05\x0a\x00\x00\x00\x00\x00\x01\x00\x07\x43\x3e\ -\x00\x00\x06\x14\x00\x01\x00\x00\x00\x01\x00\x07\x91\xe1\ -\x00\x00\x05\x34\x00\x01\x00\x00\x00\x01\x00\x07\x53\xbf\ -\x00\x00\x06\xc0\x00\x00\x00\x00\x00\x01\x00\x07\xb4\x8a\ -\x00\x00\x04\xc2\x00\x01\x00\x00\x00\x01\x00\x07\x2c\x5a\ -\x00\x00\x07\x0a\x00\x01\x00\x00\x00\x01\x00\x07\xd0\xc7\ -\x00\x00\x06\xea\x00\x01\x00\x00\x00\x01\x00\x07\xc6\x8d\ -\x00\x00\x05\x96\x00\x01\x00\x00\x00\x01\x00\x07\x77\x0b\ -\x00\x00\x07\x8c\x00\x00\x00\x00\x00\x01\x00\x07\xf2\xa5\ -\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x01\x00\x06\xf2\xed\ -\x00\x00\x04\xec\x00\x00\x00\x00\x00\x01\x00\x07\x34\x42\ -\x00\x00\x02\xda\x00\x01\x00\x00\x00\x01\x00\x06\xbe\x99\ +\x00\x00\x05\x56\x00\x01\x00\x00\x00\x01\x00\x07\x5b\xb5\ +\x00\x00\x06\xbc\x00\x01\x00\x00\x00\x01\x00\x07\xb3\x28\ +\x00\x00\x05\x9c\x00\x00\x00\x00\x00\x01\x00\x07\x6a\x2c\ +\x00\x00\x04\x12\x00\x00\x00\x00\x00\x01\x00\x07\x05\x81\ +\x00\x00\x07\xec\x00\x01\x00\x00\x00\x01\x00\x08\x0a\xfd\ +\x00\x00\x06\x8e\x00\x01\x00\x00\x00\x01\x00\x07\xac\x53\ +\x00\x00\x04\x4c\x00\x01\x00\x00\x00\x01\x00\x07\x13\xf0\ +\x00\x00\x04\x9e\x00\x01\x00\x00\x00\x01\x00\x07\x27\x77\ +\x00\x00\x03\xb6\x00\x01\x00\x00\x00\x01\x00\x06\xea\xdf\ +\x00\x00\x07\x86\x00\x00\x00\x00\x00\x01\x00\x07\xe7\xb9\ +\x00\x00\x05\xdc\x00\x00\x00\x00\x00\x01\x00\x07\x84\x06\ +\x00\x00\x05\x7c\x00\x01\x00\x00\x00\x01\x00\x07\x62\x64\ +\x00\x00\x03\x94\x00\x01\x00\x00\x00\x01\x00\x06\xe1\xe6\ +\x00\x00\x06\x62\x00\x01\x00\x00\x00\x01\x00\x07\xa1\x29\ +\x00\x00\x03\x38\x00\x01\x00\x00\x00\x01\x00\x06\xcf\xc5\ +\x00\x00\x07\x5c\x00\x01\x00\x00\x00\x01\x00\x07\xe0\x24\ +\x00\x00\x04\x7a\x00\x01\x00\x00\x00\x01\x00\x07\x1d\x48\ +\x00\x00\x03\x0e\x00\x01\x00\x00\x00\x01\x00\x06\xc6\x1d\ +\x00\x00\x03\x74\x00\x01\x00\x00\x00\x01\x00\x06\xd9\x78\ +\x00\x00\x06\x0c\x00\x01\x00\x00\x00\x01\x00\x07\x93\x74\ +\x00\x00\x05\x0a\x00\x00\x00\x00\x00\x01\x00\x07\x43\x72\ +\x00\x00\x06\x3a\x00\x01\x00\x00\x00\x01\x00\x07\x98\xc4\ +\x00\x00\x05\x34\x00\x01\x00\x00\x00\x01\x00\x07\x53\xf3\ +\x00\x00\x06\xe6\x00\x00\x00\x00\x00\x01\x00\x07\xbb\x6d\ +\x00\x00\x04\xc2\x00\x01\x00\x00\x00\x01\x00\x07\x2c\x8e\ +\x00\x00\x07\x30\x00\x01\x00\x00\x00\x01\x00\x07\xd7\xaa\ +\x00\x00\x07\x10\x00\x01\x00\x00\x00\x01\x00\x07\xcd\x70\ +\x00\x00\x05\xbc\x00\x01\x00\x00\x00\x01\x00\x07\x7d\xee\ +\x00\x00\x07\xb2\x00\x00\x00\x00\x00\x01\x00\x07\xf9\x88\ +\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x01\x00\x06\xf3\x21\ +\x00\x00\x04\xec\x00\x00\x00\x00\x00\x01\x00\x07\x34\x76\ +\x00\x00\x02\xda\x00\x01\x00\x00\x00\x01\x00\x06\xbe\xcd\ \x00\x00\x02\xb2\x00\x01\x00\x00\x00\x01\x00\x06\xb6\xfd\ " diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt index 28f691455..76d09e80d 100644 --- a/src/Mod/Arch/CMakeLists.txt +++ b/src/Mod/Arch/CMakeLists.txt @@ -3,7 +3,6 @@ SET(Arch_SRCS Init.py InitGui.py ArchComponent.py - ArchCell.py ArchWall.py importIFC.py ifcReader.py diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index 1f5f2386d..7d7fd9f5e 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -59,19 +59,21 @@ class ArchWorkbench(Workbench): " *@!!!!!$=* ", " =>!!$& ", " -+ ", - " "};""" + " "};""" MenuText = "Arch" ToolTip = "Architecture workbench" - + def Initialize(self): import DraftTools,DraftGui,Arch_rc,Arch,Draft_rc + from DraftTools import translate # arch tools self.archtools = ["Arch_Wall","Arch_Structure", "Arch_Floor","Arch_Building","Arch_Site", "Arch_Window","Arch_Roof","Arch_Axis", - "Arch_SectionPlane","Arch_Add","Arch_Remove"] + "Arch_SectionPlane","Arch_Add","Arch_Remove", + "Arch_Fixture"] self.meshtools = ["Arch_SplitMesh","Arch_MeshToShape", "Arch_SelectNonSolidMeshes","Arch_RemoveShape", "Arch_CloseHoles","Arch_MergeWalls"] @@ -90,24 +92,30 @@ class ArchWorkbench(Workbench): "Draft_SelectGroup","Draft_SelectPlane","Draft_ToggleSnap", "Draft_ShowSnapBar","Draft_ToggleGrid","Draft_UndoLine", "Draft_FinishLine","Draft_CloseLine"] + self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular', + 'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel', + 'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center', + 'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho'] - self.appendToolbar(str(DraftTools.translate("arch","Arch tools")),self.archtools) - self.appendToolbar(str(DraftTools.translate("arch","Draft tools")),self.drafttools) - self.appendToolbar(str(DraftTools.translate("arch","Draft mod tools")),self.draftmodtools) - self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Conversion Tools"))],self.meshtools) - self.appendMenu([str(DraftTools.translate("arch","&Architecture")),str(DraftTools.translate("arch","Calculation Tools"))],self.calctools) - self.appendMenu(str(DraftTools.translate("arch","&Architecture")),self.archtools) - self.appendMenu(str(DraftTools.translate("arch","&Draft")),self.drafttools+self.draftmodtools) - self.appendMenu([str(DraftTools.translate("arch","&Draft")),str(DraftTools.translate("arch","Context Tools"))],self.draftcontexttools) + self.appendToolbar(str(translate("arch","Arch tools")),self.archtools) + self.appendToolbar(str(translate("arch","Draft tools")),self.drafttools) + self.appendToolbar(str(translate("arch","Draft mod tools")),self.draftmodtools) + self.appendMenu([str(translate("arch","&Architecture")),str(translate("arch","Conversion Tools"))],self.meshtools) + self.appendMenu([str(translate("arch","&Architecture")),str(translate("arch","Calculation Tools"))],self.calctools) + self.appendMenu(str(translate("arch","&Architecture")),self.archtools) + self.appendMenu(str(translate("arch","&Draft")),self.drafttools+self.draftmodtools) + self.appendMenu([str(translate("arch","&Draft")),str(translate("arch","Context Tools"))],self.draftcontexttools) + self.appendMenu([str(translate("arch","&Draft")),str(translate("arch","Snapping"))],self.snapList) FreeCADGui.addIconPath(":/icons") FreeCADGui.addLanguagePath(":/translations") FreeCADGui.addPreferencePage(":/ui/archprefs-base.ui","Arch") - if not hasattr(FreeCADGui.draftToolBar,"loadedPreferences"): - FreeCADGui.addPreferencePage(":/ui/userprefs-base.ui","Draft") - FreeCADGui.addPreferencePage(":/ui/userprefs-import.ui","Draft") - FreeCADGui.draftToolBar.loadedPreferences = True + if hasattr(FreeCADGui,"draftToolBar"): + if not hasattr(FreeCADGui.draftToolBar,"loadedPreferences"): + FreeCADGui.addPreferencePage(":/ui/userprefs-base.ui","Draft") + FreeCADGui.addPreferencePage(":/ui/userprefs-import.ui","Draft") + FreeCADGui.draftToolBar.loadedPreferences = True Log ('Loading Arch module... done\n') - + def Activated(self): if hasattr(FreeCADGui,"draftToolBar"): FreeCADGui.draftToolBar.Activated() @@ -118,8 +126,10 @@ class ArchWorkbench(Workbench): def Deactivated(self): if hasattr(FreeCADGui,"draftToolBar"): FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.hide() Msg("Arch workbench deactivated\n") - + def ContextMenu(self, recipient): self.appendContextMenu("Draft context tools",self.draftcontexttools) @@ -127,9 +137,12 @@ class ArchWorkbench(Workbench): return "Gui::PythonWorkbench" FreeCADGui.addWorkbench(ArchWorkbench) + +# add import/export types FreeCAD.addImportType("Industry Foundation Classes (*.ifc)","importIFC") FreeCAD.addExportType("Wavefront OBJ - Arch module (*.obj)","importOBJ") FreeCAD.addExportType("WebGL file (*.html)","importWebGL") + # check for pycollada try: import collada diff --git a/src/Mod/Arch/Resources/Arch.qrc b/src/Mod/Arch/Resources/Arch.qrc index 8197a7a2c..4aafc08b0 100644 --- a/src/Mod/Arch/Resources/Arch.qrc +++ b/src/Mod/Arch/Resources/Arch.qrc @@ -31,6 +31,7 @@ icons/Arch_SelectNonManifold.svg icons/Arch_MergeWalls.svg icons/Arch_Wall_Tree_Assembly.svg + icons/Arch_Fixture.svg ui/archprefs-base.ui translations/Arch_af.qm translations/Arch_de.qm diff --git a/src/Mod/Arch/Resources/icons/Arch_Fixture.svg b/src/Mod/Arch/Resources/icons/Arch_Fixture.svg new file mode 100644 index 000000000..ee11cd0a8 --- /dev/null +++ b/src/Mod/Arch/Resources/icons/Arch_Fixture.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/Mod/Arch/Resources/ui/archprefs-base.ui b/src/Mod/Arch/Resources/ui/archprefs-base.ui index 47baea738..4741046ae 100644 --- a/src/Mod/Arch/Resources/ui/archprefs-base.ui +++ b/src/Mod/Arch/Resources/ui/archprefs-base.ui @@ -55,9 +55,9 @@ - 255 - 190 - 170 + 214 + 214 + 214 @@ -140,9 +140,9 @@ - 59 - 132 - 146 + 33 + 45 + 66 @@ -155,6 +155,47 @@
+ + + + + + Default color for window glass + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 93 + 183 + 203 + + + + WindowGlassColor + + + Mod/Arch + + + + + diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 0b0822a83..e380eb193 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -20,16 +20,7 @@ SET(Draft_SRCS ) SOURCE_GROUP("" FILES ${Draft_SRCS}) -SET(DraftLibs_SRCS - draftlibs/dxfColorMap.py - draftlibs/dxfImportObjects.py - draftlibs/dxfLibrary.py - draftlibs/dxfReader.py - draftlibs/__init__.py -) -SOURCE_GROUP("draftlibs" FILES ${DraftLibs_SRCS}) - -SET(all_files ${Draft_SRCS} ${DraftLibs_SRCS}) +SET(all_files ${Draft_SRCS}) ADD_CUSTOM_TARGET(Draft ALL SOURCES ${all_files} @@ -37,13 +28,6 @@ ADD_CUSTOM_TARGET(Draft ALL fc_copy_sources(Draft "${CMAKE_BINARY_DIR}/Mod/Draft" ${all_files}) - -INSTALL( - FILES - ${DraftLibs_SRCS} - DESTINATION - Mod/Draft/draftlibs -) INSTALL( FILES ${Draft_SRCS} diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index cd9adf39a..9f9379d50 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -158,6 +158,8 @@ def getRealName(name): def getType(obj): "getType(object): returns the Draft type of the given object" import Part + if not obj: + return None if isinstance(obj,Part.Shape): return "Shape" if "Proxy" in obj.PropertiesList: @@ -165,15 +167,17 @@ def getType(obj): return obj.Proxy.Type if obj.isDerivedFrom("Sketcher::SketchObject"): return "Sketch" + if (obj.TypeId == "Part::Line"): + return "Part::Line" if obj.isDerivedFrom("Part::Feature"): return "Part" - if (obj.Type == "App::Annotation"): + if (obj.TypeId == "App::Annotation"): return "Annotation" if obj.isDerivedFrom("Mesh::Feature"): return "Mesh" if obj.isDerivedFrom("Points::Feature"): return "Points" - if (obj.Type == "App::DocumentObjectGroup"): + if (obj.TypeId == "App::DocumentObjectGroup"): return "Group" return "Unknown" @@ -210,7 +214,7 @@ def getGroupNames(): glist = [] doc = FreeCAD.ActiveDocument for obj in doc.Objects: - if obj.Type == "App::DocumentObjectGroup": + if obj.TypeId == "App::DocumentObjectGroup": glist.append(obj.Name) return glist @@ -313,6 +317,27 @@ def printShape(shape): else: for v in shape.Vertexes: print " ",v.Point + +def compareObjects(obj1,obj2): + "Prints the differences between 2 objects" + + if obj1.TypeId != obj2.TypeId: + print obj1.Name + " and " + obj2.Name + " are of different types" + elif getType(obj1) != getType(obj2): + print obj1.Name + " and " + obj2.Name + " are of different types" + else: + for p in obj1.PropertiesList: + if p in obj2.PropertiesList: + if p in ["Shape","Label"]: + pass + elif p == "Placement": + delta = str((obj1.Placement.Base.sub(obj2.Placement.Base)).Length) + print "Objects have different placements. Distance between the 2: " + delta + " units" + else: + if getattr(obj1,p) != getattr(obj2,p): + print "Property " + p + " has a different value" + else: + print "Property " + p + " doesn't exist in one of the objects" def formatObject(target,origin=None): ''' @@ -381,13 +406,48 @@ def select(objs=None): for obj in objs: FreeCADGui.Selection.addSelection(obj) -def loadTexture(filename): - "loadTexture(filename): returns a SoSFImage from a file" +def loadSvgPatterns(): + "loads the default Draft SVG patterns and custom patters if available" + import importSVG + from PyQt4 import QtCore + FreeCAD.svgpatterns = {} + # getting default patterns + patfiles = QtCore.QDir(":/patterns").entryList() + for fn in patfiles: + f = QtCore.QFile(":/patterns/"+str(fn)) + f.open(QtCore.QIODevice.ReadOnly) + p = importSVG.getContents(str(f.readAll()),'pattern',True) + if p: + FreeCAD.svgpatterns.update(p) + # looking for user patterns + altpat = getParam("patternFile") + if os.path.isdir(altpat): + for f in os.listdir(altpat): + if f[-4:].upper() == ".SVG": + p = importSVG.getContents(altpat+os.sep+f,'pattern') + if p: + FreeCAD.svgpatterns.update(p) + +def loadTexture(filename,size=None): + """loadTexture(filename,[size]): returns a SoSFImage from a file. If size + is defined (an int or a tuple), and provided the input image is a png file, + it will be scaled to match the given size.""" if gui: from pivy import coin - from PyQt4 import QtGui + from PyQt4 import QtGui,QtSvg try: - p = QtGui.QImage(filename) + if size and (".svg" in filename.lower()): + # we need to resize + if isinstance(size,int): + size = (size,size) + svgr = QtSvg.QSvgRenderer(filename) + p = QtGui.QImage(size[0],size[1],QtGui.QImage.Format_ARGB32_Premultiplied) + pa = QtGui.QPainter() + pa.begin(p) + svgr.render(pa) + pa.end() + else: + p = QtGui.QImage(filename) size = coin.SbVec2s(p.width(), p.height()) buffersize = p.numBytes() numcomponents = int (buffersize / ( size[0] * size[1] )) @@ -419,6 +479,7 @@ def loadTexture(filename): img.setValue(size, numcomponents, bytes) except: + print "Draft: unable to load texture" return None else: return img @@ -680,61 +741,61 @@ def makeText(stringslist,point=Vector(0,0,0),screen=False): def makeCopy(obj,force=None,reparent=False): '''makeCopy(object): returns an exact copy of an object''' if (getType(obj) == "Rectangle") or (force == "Rectangle"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Rectangle(newobj) if gui: _ViewProviderRectangle(newobj.ViewObject) elif (getType(obj) == "Dimension") or (force == "Dimension"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Dimension(newobj) if gui: _ViewProviderDimension(newobj.ViewObject) elif (getType(obj) == "Wire") or (force == "Wire"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Wire(newobj) if gui: _ViewProviderWire(newobj.ViewObject) elif (getType(obj) == "Circle") or (force == "Circle"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Circle(newobj) if gui: _ViewProviderDraft(newobj.ViewObject) elif (getType(obj) == "Polygon") or (force == "Polygon"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Polygon(newobj) if gui: _ViewProviderDraft(newobj.ViewObject) elif (getType(obj) == "BSpline") or (force == "BSpline"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _BSpline(newobj) if gui: _ViewProviderBSpline(newobj.ViewObject) elif (getType(obj) == "Block") or (force == "BSpline"): - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) _Block(newobj) if gui: _ViewProviderDraftPart(newobj.ViewObject) elif (getType(obj) == "Structure") or (force == "Structure"): import ArchStructure - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) ArchStructure._Structure(newobj) if gui: ArchStructure._ViewProviderStructure(newobj.ViewObject) elif (getType(obj) == "Wall") or (force == "Wall"): import ArchWall - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) ArchWall._Wall(newobj) if gui: ArchWall._ViewProviderWall(newobj.ViewObject) elif (getType(obj) == "Window") or (force == "Window"): import ArchWindow - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) ArchWindow._Window(newobj) if gui: Archwindow._ViewProviderWindow(newobj.ViewObject) elif (getType(obj) == "Cell") or (force == "Cell"): import ArchCell - newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name)) + newobj = FreeCAD.ActiveDocument.addObject(obj.TypeId,getRealName(obj.Name)) ArchCell._Cell(newobj) if gui: ArchCell._ViewProviderCell(newobj.ViewObject) @@ -758,7 +819,7 @@ def makeCopy(obj,force=None,reparent=False): parents = obj.InList if parents: for par in parents: - if par.Type == "App::DocumentObjectGroup": + if par.TypeId == "App::DocumentObjectGroup": par.addObject(newobj) else: for prop in par.PropertiesList: @@ -1067,7 +1128,7 @@ def scale(objectslist,delta=Vector(1,1,1),center=Vector(0,0,0),copy=False,legacy newobj.Points = p elif (obj.isDerivedFrom("Part::Feature")): newobj.Shape = sh - elif (obj.Type == "App::Annotation"): + elif (obj.TypeId == "App::Annotation"): factor = delta.x * delta.y * delta.z * obj.ViewObject.FontSize obj.ViewObject.Fontsize = factor if copy: formatObject(newobj,obj) @@ -1284,15 +1345,16 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct def getLineStyle(obj): "returns a linestyle pattern for a given object" - if obj.ViewObject: - if hasattr(obj.ViewObject,"DrawStyle"): - ds = obj.ViewObject.DrawStyle - if ds == "Dashed": - return "0.09,0.05" - elif ds == "Dashdot": - return "0.09,0.05,0.02,0.05" - elif ds == "Dotted": - return "0.02,0.02" + if gui: + if obj.ViewObject: + if hasattr(obj.ViewObject,"DrawStyle"): + ds = obj.ViewObject.DrawStyle + if ds == "Dashed": + return "0.09,0.05" + elif ds == "Dashdot": + return "0.09,0.05,0.02,0.05" + elif ds == "Dotted": + return "0.02,0.02" return "none" def getProj(vec): @@ -1306,6 +1368,8 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct return Vector(lx,ly,0) def getPattern(pat): + if not hasattr(FreeCAD,"svgpatterns"): + loadSvgPatterns() if pat in FreeCAD.svgpatterns: return FreeCAD.svgpatterns[pat] return '' @@ -1359,8 +1423,11 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct svg += ';fill:' + fill + '"' svg += '/>\n' return svg + + if not obj: + pass - if getType(obj) == "Dimension": + elif getType(obj) == "Dimension": p1,p2,p3,p4,tbase,norm,rot = obj.ViewObject.Proxy.calcGeom(obj) dimText = getParam("dimPrecision") dimText = "%."+str(dimText)+"f" @@ -1485,23 +1552,36 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct n += 1 elif obj.isDerivedFrom('Part::Feature'): - if obj.Shape.isNull(): return '' - color = getrgb(obj.ViewObject.LineColor) + if obj.Shape.isNull(): + return '' + if gui: + color = getrgb(obj.ViewObject.LineColor) + else: + color = "#000000" # setting fill - if obj.Shape.Faces and (obj.ViewObject.DisplayMode != "Wireframe"): - if fillstyle == "shape color": - fill = getrgb(obj.ViewObject.ShapeColor) + if obj.Shape.Faces: + if gui: + if (obj.ViewObject.DisplayMode != "Wireframe"): + if fillstyle == "shape color": + fill = getrgb(obj.ViewObject.ShapeColor) + else: + fill = 'url(#'+fillstyle+')' + svg += getPattern(fillstyle) + else: + fill = "none" else: - fill = 'url(#'+fillstyle+')' - svg += getPattern(fillstyle) + fill = "#888888" else: fill = 'none' lstyle = getLineStyle(obj) name = obj.Name - if obj.ViewObject.DisplayMode == "Shaded": - stroke = "none" + if gui: + if obj.ViewObject.DisplayMode == "Shaded": + stroke = "none" + else: + stroke = getrgb(obj.ViewObject.LineColor) else: - stroke = getrgb(obj.ViewObject.LineColor) + stroke = "#000000" if len(obj.Shape.Vertexes) > 1: wiredEdges = [] @@ -1699,13 +1779,6 @@ def makeShapeString(String,FontFile,Size = 100,Tracking = 0): into a Compound Shape''' # temporary code - import platform - if not (platform.system() == 'Linux'): -# if (platform.system() == 'Linux'): - FreeCAD.Console.PrintWarning("Sorry, ShapeString is not yet implemented for your platform.\n") - return (None) - # temporary code - obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","ShapeString") _ShapeString(obj) obj.String = String @@ -1759,7 +1832,7 @@ def heal(objlist=None,delete=True,reparent=True): for obj in objlist: dtype = getType(obj) - ftype = obj.Type + ftype = obj.TypeId if ftype in ["Part::FeaturePython","App::FeaturePython","Part::Part2DObjectPython"]: if obj.ViewObject.Proxy == 1 and dtype in ["Unknown","Part"]: got = True @@ -2008,7 +2081,7 @@ def upgrade(objects,delete=False,force=None): loneedges = [] meshes = [] for ob in objects: - if ob.Type == "App::DocumentObjectGroup": + if ob.TypeId == "App::DocumentObjectGroup": groups.append(ob) elif ob.isDerivedFrom("Part::Feature"): parts.append(ob) @@ -2997,10 +3070,15 @@ class _ViewProviderRectangle(_ViewProviderDraft): def onChanged(self, vp, prop): from pivy import coin + from PyQt4 import QtCore if prop == "TextureImage": r = vp.RootNode - if os.path.exists(vp.TextureImage): - im = loadTexture(vp.TextureImage) + i = QtCore.QFileInfo(vp.TextureImage) + if i.exists(): + size = None + if ":/patterns" in vp.TextureImage: + size = 128 + im = loadTexture(vp.TextureImage, size) if im: self.texture = coin.SoTexture2() self.texture.image = im @@ -3098,7 +3176,7 @@ class _Wire(_DraftObject): def updateProps(self,fp): "sets the start and end properties" pl = FreeCAD.Placement(fp.Placement) - if len(fp.Points) == 2: + if len(fp.Points) >= 2: displayfpstart = pl.multVec(fp.Points[0]) displayfpend = pl.multVec(fp.Points[-1]) if fp.Start != displayfpstart: @@ -3168,7 +3246,10 @@ class _Wire(_DraftObject): w = DraftGeomUtils.filletWire(shape,fp.FilletRadius) if w: shape = w - shape = Part.Face(shape) + try: + shape = Part.Face(shape) + except: + pass else: edges = [] pts = fp.Points[1:] @@ -3289,6 +3370,8 @@ class _DrawingView(_DraftObject): obj.addProperty("App::PropertyLink","Source","Base","The linked object") obj.addProperty("App::PropertyEnumeration","FillStyle","Drawing View","Shape Fill Style") fills = ['shape color'] + if not hasattr(FreeCAD,"svgpatterns"): + loadSvgPatterns() for f in FreeCAD.svgpatterns.keys(): fills.append(f) obj.FillStyle = fills @@ -3734,7 +3817,7 @@ class _ShapeString(_DraftObject): def __init__(self, obj): _DraftObject.__init__(self,obj,"ShapeString") obj.addProperty("App::PropertyString","String","Base","Text string") - obj.addProperty("App::PropertyString","FontFile","Base","Font file name") + obj.addProperty("App::PropertyFile","FontFile","Base","Font file name") obj.addProperty("App::PropertyFloat","Size","Base","Height of text") obj.addProperty("App::PropertyInteger","Tracking","Base", "Inter-character spacing") @@ -3771,13 +3854,53 @@ class _ShapeString(_DraftObject): # whitespace (ex: ' ') has no faces. This breaks OpenSCAD2Dgeom... if CharFaces: # s = OpenSCAD2Dgeom.Overlappingfaces(CharFaces).makeshape() - s = self.makeGlyph(CharFaces) + #s = self.makeGlyph(CharFaces) + s = self.makeFaces(char) SSChars.append(s) shape = Part.Compound(SSChars) fp.Shape = shape if plm: fp.Placement = plm - + + def makeFaces(self, wireChar): + import Part + compFaces=[] + wirelist=sorted(wireChar,key=(lambda shape: shape.BoundBox.DiagonalLength),reverse=True) + fixedwire = [] + for w in wirelist: + compEdges = Part.Compound(w.Edges) + compEdges = compEdges.connectEdgesToWires() + fixedwire.append(compEdges.Wires[0]) + wirelist = fixedwire + + sep_wirelist = [] + while len(wirelist) > 0: + wire2Face = [wirelist[0]] + face = Part.Face(wirelist[0]) + for w in wirelist[1:]: + p = w.Vertexes[0].Point + u,v = face.Surface.parameter(p) + if face.isPartOfDomain(u,v): + f = Part.Face(w) + if face.Orientation == f.Orientation: + if f.Surface.Axis * face.Surface.Axis < 0: + w.reverse() + else: + if f.Surface.Axis * face.Surface.Axis > 0: + w.reverse() + wire2Face.append(w) + else: + sep_wirelist.append(w) + wirelist = sep_wirelist + sep_wirelist = [] + face = Part.Face(wire2Face) + face.validate() + if face.Surface.Axis.z < 0.0: + face.reverse() + compFaces.append(face) + ret = Part.Compound(compFaces) + return ret + def makeGlyph(self, facelist): ''' turn list of simple contour faces into a compound shape representing a glyph ''' ''' remove cuts, fuse overlapping contours, retain islands ''' @@ -3806,7 +3929,11 @@ class _ShapeString(_DraftObject): else: # partial overlap - (font designer error?) result = result.fuse(face) - glyphfaces = [result] + #glyphfaces = [result] + wl = result.Wires + for w in wl: + w.fixWire() + glyphfaces = [Part.Face(wl)] glyphfaces.extend(islands) ret = Part.Compound(glyphfaces) # should we fuse these instead of making compound? return ret diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 1ae0d693f..1f81d6ed3 100755 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -301,6 +301,8 @@ def findIntersection(edge1,edge2,infinite1=False,infinite2=False,ex1=False,ex2=F if dirVec.dot(arc.Curve.Axis) != 0 : toPlane = Vector(arc.Curve.Axis) ; toPlane.normalize() d = pt1.dot(toPlane) + if not d: + return [] dToPlane = center.sub(pt1).dot(toPlane) toPlane = Vector(pt1) toPlane.scale(dToPlane/d,dToPlane/d,dToPlane/d) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 1ea342253..ff4d9e0da 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -33,7 +33,7 @@ Report to Draft.py for info import FreeCAD, FreeCADGui, os, Draft, sys try: - from PyQt4 import QtCore,QtGui,QtSvg + from PyQt4 import QtCore,QtGui,QtSvg except: FreeCAD.Console.PrintMessage("Error: Python-qt4 package must be installed on your system to use the Draft module.") @@ -559,7 +559,11 @@ class DraftToolBar: FreeCADGui.ActiveDocument.resetEdit() return True todo.delay(FreeCADGui.Control.showDialog,dummy()) - self.setTitle(title) + self.setTitle(title) + + def redraw(self): + "utility function that is performed after each clicked point" + self.checkLocal() def selectPlaneUi(self): self.taskUi(translate("draft", "Select Plane")) @@ -623,7 +627,7 @@ class DraftToolBar: self.taskUi(title,extra,icon) self.xValue.setEnabled(True) self.yValue.setEnabled(True) - self.labelx.setText(translate("draft", "X")) + self.checkLocal() self.labelx.show() self.labely.show() self.labelz.show() @@ -805,6 +809,17 @@ class DraftToolBar: def vertUi(self,addmode=True): self.addButton.setChecked(addmode) self.delButton.setChecked(not(addmode)) + + def checkLocal(self): + "checks if x,y,z coords must be displayed as local or global" + self.labelx.setText(translate("draft", "Global X")) + self.labely.setText(translate("draft", "Global Y")) + self.labelz.setText(translate("draft", "Global Z")) + if hasattr(FreeCAD,"DraftWorkingPlane"): + if not FreeCAD.DraftWorkingPlane.isGlobal(): + self.labelx.setText(translate("draft", "Local X")) + self.labely.setText(translate("draft", "Local Y")) + self.labelz.setText(translate("draft", "Local Z")) def setEditButtons(self,mode): self.addButton.setEnabled(mode) @@ -868,7 +883,7 @@ class DraftToolBar: b = float(self.color.blue()/255.0) col = (r,g,b,0.0) for i in FreeCADGui.Selection.getSelection(): - if (i.Type == "App::Annotation"): + if (i.TypeId == "App::Annotation"): i.ViewObject.TextColor=col else: if "LineColor" in i.ViewObject.PropertiesList: @@ -1191,6 +1206,8 @@ class DraftToolBar: dp = plane.getLocalRot(FreeCAD.Vector(point.x-last.x, point.y-last.y, point.z-last.z)) else: dp = FreeCAD.Vector(point.x-last.x, point.y-last.y, point.z-last.z) + elif plane: + dp = plane.getLocalCoords(point) # set widgets if self.mask in ['y','z']: diff --git a/src/Mod/Draft/DraftSnap.py b/src/Mod/Draft/DraftSnap.py index bedb71f13..9f5d98202 100644 --- a/src/Mod/Draft/DraftSnap.py +++ b/src/Mod/Draft/DraftSnap.py @@ -27,7 +27,6 @@ __url__ = "http://free-cad.sourceforge.net" import FreeCAD, FreeCADGui, math, Draft, DraftGui, DraftTrackers, DraftVecUtils -from DraftGui import todo,getMainWindow from FreeCAD import Vector from pivy import coin from PyQt4 import QtCore,QtGui @@ -72,11 +71,15 @@ class Snapper: self.constrainLine = None self.trackLine = None self.radiusTracker = None + self.dim1 = None + self.dim2 = None self.snapInfo = None self.lastSnappedObject = None + self.lastArchPoint = None self.active = True self.forceGridOff = False - self.trackers = [[],[],[],[],[]] # view, grid, snap, extline, radius + # the trackers are stored in lists because there can be several views, each with its own set + self.trackers = [[],[],[],[],[],[],[]] # view, grid, snap, extline, radius, dim1, dim2 self.polarAngles = [90,45] @@ -86,9 +89,9 @@ class Snapper: 'parallel':'circle', 'grid':'circle', 'endpoint':'dot', - 'midpoint':'dot', + 'midpoint':'square', 'perpendicular':'dot', - 'angle':'dot', + 'angle':'square', 'center':'dot', 'ortho':'dot', 'intersection':'dot'} @@ -118,7 +121,7 @@ class Snapper: if not hasattr(self,"toolbar"): self.makeSnapToolBar() - mw = getMainWindow() + mw = DraftGui.getMainWindow() bt = mw.findChild(QtGui.QToolBar,"Draft Snap") if not bt: mw.addToolBar(self.toolbar) @@ -173,6 +176,10 @@ class Snapper: self.extLine.off() if self.trackLine: self.trackLine.off() + if self.dim1: + self.dim1.off() + if self.dim2: + self.dim2.off() point = self.getApparentPoint(screenpos[0],screenpos[1]) @@ -200,6 +207,9 @@ class Snapper: if self.trackLine and lastpoint and (not noTracker): self.trackLine.p2(fp) self.trackLine.on() + # set the arch point tracking + if self.lastArchPoint: + self.setArchDims(self.lastArchPoint,fp) return fp else: @@ -232,6 +242,7 @@ class Snapper: comp = self.snapInfo['Component'] if (Draft.getType(obj) == "Wall") and not oldActive: + # special snapping for wall: only to its base shape (except when CTRL is pressed) edges = [] for o in [obj]+obj.Additions: if Draft.getType(o) == "Wall": @@ -243,8 +254,24 @@ class Snapper: snaps.extend(self.snapToPerpendicular(edge,lastpoint)) snaps.extend(self.snapToIntersection(edge)) snaps.extend(self.snapToElines(edge,eline)) - + + elif (Draft.getType(obj) == "Structure") and not oldActive: + # special snapping for struct: only to its base point (except when CTRL is pressed) + if obj.Base: + for edge in o.Base.Shape.Edges: + snaps.extend(self.snapToEndpoints(edge)) + snaps.extend(self.snapToMidpoint(edge)) + snaps.extend(self.snapToPerpendicular(edge,lastpoint)) + snaps.extend(self.snapToIntersection(edge)) + snaps.extend(self.snapToElines(edge,eline)) + else: + b = obj.Placement.Base + snaps.append([b,'endpoint',b]) + elif obj.isDerivedFrom("Part::Feature"): + if Draft.getType(obj) == "Polygon": + snaps.extend(self.snapToPolygon(obj)) + if (not self.maxEdges) or (len(obj.Edges) <= self.maxEdges): if "Edge" in comp: # we are snapping to an edge @@ -327,7 +354,15 @@ class Snapper: self.trackLine.on() # set the cursor self.setCursor(winner[1]) - + + # set the arch point tracking + if self.lastArchPoint: + self.setArchDims(self.lastArchPoint,fp) + if Draft.getType(obj) in ["Wall","Structure"]: + self.lastArchPoint = winner[2] + else: + self.lastArchPoint = None + # return the final point return fp @@ -618,6 +653,19 @@ class Snapper: for p in pt: snaps.append([p,'intersection',p]) return snaps + + def snapToPolygon(self,obj): + "returns a list of polygon center snap locations" + snaps = [] + c = obj.Placement.Base + for edge in obj.Shape.Edges: + p1 = edge.Vertexes[0].Point + p2 = edge.Vertexes[-1].Point + v1 = p1.add((p2-p1).scale(.25,.25,.25)) + v2 = p1.add((p2-p1).scale(.75,.75,.75)) + snaps.append([v1,'center',c]) + snaps.append([v2,'center',c]) + return snaps def snapToVertex(self,info,active=False): p = Vector(info['x'],info['y'],info['z']) @@ -644,6 +692,21 @@ class Snapper: nv = DraftVecUtils.project(dv,DraftGeomUtils.vec(edge)) np = (edge.Vertexes[0].Point).add(nv) return np + + def setArchDims(self,p1,p2): + "show arch dimensions between 2 points" + if not self.dim1: + self.dim1 = DraftTrackers.archDimTracker(mode=2) + if not self.dim2: + self.dim1 = DraftTrackers.archDimTracker(mode=3) + self.dim1.p1(p1) + self.dim2.p1(p1) + self.dim1.p2(p2) + self.dim2.p2(p2) + if self.dim1.Distance: + self.dim1.on() + if self.dim2.Distance: + self.dim2.on() def setCursor(self,mode=None): "setCursor(self,mode=None): sets or resets the cursor to the given mode or resets" @@ -687,6 +750,10 @@ class Snapper: self.extLine.off() if self.radiusTracker: self.radiusTracker.off() + if self.dim1: + self.dim1.off() + if self.dim2: + self.dim2.off() if self.grid: if not Draft.getParam("alwaysShowGrid"): self.grid.off() @@ -696,6 +763,7 @@ class Snapper: if Draft.getParam("hideSnapBar"): self.toolbar.hide() self.mask = None + self.lastArchPoint = None def constrain(self,point,basepoint=None,axis=None): '''constrain(point,basepoint=None,axis=None: Returns a @@ -851,7 +919,7 @@ class Snapper: "builds the Snap toolbar" self.toolbar = QtGui.QToolBar(None) self.toolbar.setObjectName("Draft Snap") - self.toolbar.setWindowTitle("Draft Snap") + self.toolbar.setWindowTitle(QtCore.QCoreApplication.translate("Workbench", "Draft Snap")) self.toolbarButtons = [] self.masterbutton = QtGui.QPushButton(None) self.masterbutton.setIcon(QtGui.QIcon(":/icons/Snap_Lock.svg")) @@ -929,14 +997,21 @@ class Snapper: "shows the toolbar and the grid" if not hasattr(self,"toolbar"): self.makeSnapToolBar() - mw = getMainWindow() + mw = DraftGui.getMainWindow() bt = mw.findChild(QtGui.QToolBar,"Draft Snap") if not bt: mw.addToolBar(self.toolbar) + self.toolbar.setParent(mw) self.toolbar.show() + self.toolbar.toggleViewAction().setVisible(True) if FreeCADGui.ActiveDocument: self.setTrackers() + def hide(self): + if hasattr(self,"toolbar"): + self.toolbar.hide() + self.toolbar.toggleViewAction().setVisible(True) + def setGrid(self): "sets the grid, if visible" if self.grid and (not self.forceGridOff): @@ -952,6 +1027,8 @@ class Snapper: self.tracker = self.trackers[2][i] self.extLine = self.trackers[3][i] self.radiusTracker = self.trackers[4][i] + self.dim1 = self.trackers[5][i] + self.dim2 = self.trackers[6][i] else: if Draft.getParam("grid"): self.grid = DraftTrackers.gridTracker() @@ -960,11 +1037,15 @@ class Snapper: self.tracker = DraftTrackers.snapTracker() self.extLine = DraftTrackers.lineTracker(dotted=True) self.radiusTracker = DraftTrackers.radiusTracker() + self.dim1 = DraftTrackers.archDimTracker(mode=2) + self.dim2 = DraftTrackers.archDimTracker(mode=3) self.trackers[0].append(v) self.trackers[1].append(self.grid) self.trackers[2].append(self.tracker) self.trackers[3].append(self.extLine) self.trackers[4].append(self.radiusTracker) + self.trackers[5].append(self.dim1) + self.trackers[6].append(self.dim2) if self.grid and (not self.forceGridOff): self.grid.set() diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 33fb5d6ca..fe67b50f9 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -29,7 +29,7 @@ __url__ = "http://free-cad.sourceforge.net" # Generic stuff #--------------------------------------------------------------------------- -import os, FreeCAD, FreeCADGui, WorkingPlane, math, re, importSVG, Draft, Draft_rc, DraftVecUtils +import os, FreeCAD, FreeCADGui, WorkingPlane, math, re, Draft, Draft_rc, DraftVecUtils from FreeCAD import Vector from DraftGui import todo,QtCore,QtGui from DraftSnap import * @@ -43,16 +43,6 @@ from pivy import coin # update the translation engine FreeCADGui.updateLocale() -# loads the fill patterns -FreeCAD.svgpatterns = importSVG.getContents(Draft_rc.qt_resource_data,'pattern',True) -altpat = Draft.getParam("patternFile") -if os.path.isdir(altpat): - for f in os.listdir(altpat): - if f[-4:].upper() == ".SVG": - p = importSVG.getContents(altpat+os.sep+f,'pattern') - if p: - FreeCAD.svgpatterns.update(p) - # sets the default working plane plane = WorkingPlane.plane() FreeCAD.DraftWorkingPlane = plane @@ -242,9 +232,12 @@ class DraftTool: self.ui.sourceCmd = self self.ui.setTitle(name) self.ui.show() - rot = self.view.getCameraNode().getField("orientation").getValue() - upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) - plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) + try: + rot = self.view.getCameraNode().getField("orientation").getValue() + upv = Vector(rot.multVec(coin.SbVec3f(0,1,0)).getValue()) + plane.setup(DraftVecUtils.neg(self.view.getViewDirection()), Vector(0,0,0), upv) + except: + pass self.node = [] self.pos = [] self.constrain = None @@ -504,6 +497,7 @@ class Line(Creator): if (not self.node) and (not self.support): self.support = getSupport(arg) if self.point: + self.ui.redraw() self.pos = arg["Position"] self.node.append(self.point) self.drawSegment(self.point) @@ -617,6 +611,7 @@ class BSpline(Line): if (not self.node) and (not self.support): self.support = getSupport(arg) if self.point: + self.ui.redraw() self.pos = arg["Position"] self.node.append(self.point) self.drawUpdate(self.point) @@ -824,6 +819,7 @@ class Rectangle(Creator): if (not self.node) and (not self.support): self.support = getSupport(arg) if self.point: + self.ui.redraw() self.appendPoint(self.point) def numericInput(self,numx,numy,numz): @@ -1416,6 +1412,7 @@ class Ellipse(Creator): if (not self.node) and (not self.support): self.support = getSupport(arg) if self.point: + self.ui.redraw() self.appendPoint(self.point) def numericInput(self,numx,numy,numz): @@ -1700,6 +1697,7 @@ class Dimension(Creator): if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): import DraftGeomUtils if self.point: + self.ui.redraw() if (not self.node) and (not self.support): self.support = getSupport(arg) if hasMod(arg,MODALT) and (len(self.node)<3): @@ -1795,7 +1793,7 @@ class ShapeString(Creator): def GetResources(self): return {'Pixmap' : 'Draft_ShapeString', 'Accel' : "S, S", - 'MenuShapeString': QtCore.QT_TRANSLATE_NOOP("Draft_ShapeString", "ShapeString"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_ShapeString", "Shape from text..."), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_ShapeString", "Creates text string in shapes.")} def Activated(self): @@ -1817,12 +1815,11 @@ class ShapeString(Creator): "creates object in the current doc" # print "debug: D_T ShapeString.createObject type(self.SString): " str(type(self.SString)) # temporary code - import platform - if not (platform.system() == 'Linux'): -# if (platform.system() == 'Linux'): - FreeCAD.Console.PrintWarning("Sorry, ShapeString is not yet fully implemented for your platform.\n") - self.finish() - return + #import platform + #if not (platform.system() == 'Linux'): + # FreeCAD.Console.PrintWarning("Sorry, ShapeString is not yet fully implemented for your platform.\n") + # self.finish() + # return # temporary code dquote = '"' @@ -1999,6 +1996,7 @@ class Move(Modifier): elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): if self.point: + self.ui.redraw() if (self.node == []): self.node.append(self.point) self.ui.isRelative.show() @@ -2543,7 +2541,7 @@ class Trimex(Modifier): sw = self.obj.ViewObject.LineWidth import DraftGeomUtils for e in self.edges: - if DraftGeomUtils(e) == "Line": + if DraftGeomUtils.geomType(e) == "Line": self.ghost.append(lineTracker(scolor=sc,swidth=sw)) else: self.ghost.append(arcTracker(scolor=sc,swidth=sw)) @@ -2711,7 +2709,7 @@ class Trimex(Modifier): ghost.setRadius(edge.Curve.Radius) if real: newedges.append(edge) ghost.on() - + # finishing if real: return newedges else: return dist @@ -2731,12 +2729,39 @@ class Trimex(Modifier): self.doc.openTransaction("Trim/extend") if Draft.getType(self.obj) in ["Wire","BSpline"]: p = [] - if self.placement: invpl = self.placement.inverse() + if self.placement: + invpl = self.placement.inverse() for v in newshape.Vertexes: np = v.Point - if self.placement: np = invpl.multVec(np) + if self.placement: + np = invpl.multVec(np) p.append(np) self.obj.Points = p + elif Draft.getType(self.obj) == "Part::Line": + p = [] + if self.placement: + invpl = self.placement.inverse() + for v in newshape.Vertexes: + np = v.Point + if self.placement: + np = invpl.multVec(np) + p.append(np) + if ((p[0].x == self.obj.X1) and (p[0].y == self.obj.Y1) and (p[0].z == self.obj.Z1)): + self.obj.X2 = p[-1].x + self.obj.Y2 = p[-1].y + self.obj.Z2 = p[-1].z + elif ((p[-1].x == self.obj.X1) and (p[-1].y == self.obj.Y1) and (p[-1].z == self.obj.Z1)): + self.obj.X2 = p[0].x + self.obj.Y2 = p[0].y + self.obj.Z2 = p[0].z + elif ((p[0].x == self.obj.X2) and (p[0].y == self.obj.Y2) and (p[0].z == self.obj.Z2)): + self.obj.X1 = p[-1].x + self.obj.Y1 = p[-1].y + self.obj.Z1 = p[-1].z + else: + self.obj.X1 = p[0].x + self.obj.Y1 = p[0].y + self.obj.Z1 = p[0].z elif Draft.getType(self.obj) == "Circle": angles = self.ghost[0].getAngles() print "original",self.obj.FirstAngle," ",self.obj.LastAngle @@ -2749,7 +2774,7 @@ class Trimex(Modifier): self.doc.commitTransaction() for g in self.ghost: g.off() - def finish(self,closed=False): + def finish(self,closed=False): Modifier.finish(self) self.force = None if self.ui: @@ -2853,6 +2878,7 @@ class Scale(Modifier): elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): if self.point: + self.ui.redraw() if (self.node == []): self.node.append(self.point) self.ui.isRelative.show() @@ -3113,6 +3139,7 @@ class Edit(Modifier): self.update(self.trackers[self.editing].get()) elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): + self.ui.redraw() if self.editing == None: sel = FreeCADGui.Selection.getSelectionEx() if sel: @@ -3688,8 +3715,154 @@ class Heal(): else: Draft.heal() FreeCAD.ActiveDocument.commitTransaction() + +#--------------------------------------------------------------------------- +# Snap tools +#--------------------------------------------------------------------------- + +class Draft_Snap_Lock(): + def GetResources(self): + return {'Pixmap' : 'Snap_Lock', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Lock", "Toggle On/Off"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Lock", "Activates/deactivates all snap tools at once")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"masterbutton"): + print FreeCADGui.Snapper.masterbutton + FreeCADGui.Snapper.masterbutton.toggle() + +class Draft_Snap_Midpoint(): + def GetResources(self): + return {'Pixmap' : 'Snap_Midpoint', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Midpoint", "Midpoint"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Midpoint", "Snaps to midpoints of edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonmidpoint": + b.toggle() + +class Draft_Snap_Perpendicular(): + def GetResources(self): + return {'Pixmap' : 'Snap_Perpendicular', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Perpendicular", "Perpendicular"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Perpendicular", "Snaps to perpendicular points on edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonperpendicular": + b.toggle() + +class Draft_Snap_Grid(): + def GetResources(self): + return {'Pixmap' : 'Snap_Grid', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Grid", "Grid"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Grid", "Snaps to grid points")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtongrid": + b.toggle() + +class Draft_Snap_Intersection(): + def GetResources(self): + return {'Pixmap' : 'Snap_Intersection', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Intersection", "Intersection"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Intersection", "Snaps to edges intersections")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonintersection": + b.toggle() + +class Draft_Snap_Parallel(): + def GetResources(self): + return {'Pixmap' : 'Snap_Parallel', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Parallel", "Parallel"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Parallel", "Snaps to parallel directions of edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonparallel": + b.toggle() + +class Draft_Snap_Endpoint(): + def GetResources(self): + return {'Pixmap' : 'Snap_Endpoint', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Endpoint", "Endpoint"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Endpoint", "Snaps to endpoints of edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonendpoint": + b.toggle() + +class Draft_Snap_Angle(): + def GetResources(self): + return {'Pixmap' : 'Snap_Angle', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Angle", "Angles"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Angle", "Snaps to 45 and 90 degrees points on arcs and circles")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonangle": + b.toggle() + +class Draft_Snap_Center(): + def GetResources(self): + return {'Pixmap' : 'Snap_Center', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Center", "Center"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Center", "Snaps to center of circles and arcs")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtoncenter": + b.toggle() + +class Draft_Snap_Extension(): + def GetResources(self): + return {'Pixmap' : 'Snap_Extension', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Extension", "Extension"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Extension", "Snaps to extension of edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonextension": + b.toggle() + +class Draft_Snap_Near(): + def GetResources(self): + return {'Pixmap' : 'Snap_Near', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Near", "Nearest"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Near", "Snaps to nearest point on edges")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonnear": + b.toggle() + +class Draft_Snap_Ortho(): + def GetResources(self): + return {'Pixmap' : 'Snap_Ortho', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Ortho", "Ortho"), + 'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Draft_Snap_Ortho", "Snaps to orthogonal and 45 degrees directions")} + def Activated(self): + if hasattr(FreeCADGui,"Snapper"): + if hasattr(FreeCADGui.Snapper,"toolbarButtons"): + for b in FreeCADGui.Snapper.toolbarButtons: + if b.objectName() == "SnapButtonortho": + b.toggle() - #--------------------------------------------------------------------------- # Adds the icons & commands to the FreeCAD command manager, and sets defaults #--------------------------------------------------------------------------- @@ -3742,6 +3915,20 @@ FreeCADGui.addCommand('Draft_ToggleSnap',ToggleSnap()) FreeCADGui.addCommand('Draft_ShowSnapBar',ShowSnapBar()) FreeCADGui.addCommand('Draft_ToggleGrid',ToggleGrid()) +# snap commands +FreeCADGui.addCommand('Draft_Snap_Lock',Draft_Snap_Lock()) +FreeCADGui.addCommand('Draft_Snap_Midpoint',Draft_Snap_Midpoint()) +FreeCADGui.addCommand('Draft_Snap_Perpendicular',Draft_Snap_Perpendicular()) +FreeCADGui.addCommand('Draft_Snap_Grid',Draft_Snap_Grid()) +FreeCADGui.addCommand('Draft_Snap_Intersection',Draft_Snap_Intersection()) +FreeCADGui.addCommand('Draft_Snap_Parallel',Draft_Snap_Parallel()) +FreeCADGui.addCommand('Draft_Snap_Endpoint',Draft_Snap_Endpoint()) +FreeCADGui.addCommand('Draft_Snap_Angle',Draft_Snap_Angle()) +FreeCADGui.addCommand('Draft_Snap_Center',Draft_Snap_Center()) +FreeCADGui.addCommand('Draft_Snap_Extension',Draft_Snap_Extension()) +FreeCADGui.addCommand('Draft_Snap_Near',Draft_Snap_Near()) +FreeCADGui.addCommand('Draft_Snap_Ortho',Draft_Snap_Ortho()) + # a global place to look for active draft Command FreeCAD.activeDraftCommand = None diff --git a/src/Mod/Draft/DraftTrackers.py b/src/Mod/Draft/DraftTrackers.py index 189819ca4..960f281d2 100644 --- a/src/Mod/Draft/DraftTrackers.py +++ b/src/Mod/Draft/DraftTrackers.py @@ -28,7 +28,6 @@ __url__ = "http://free-cad.sourceforge.net" import FreeCAD,FreeCADGui,math,Draft, DraftVecUtils from FreeCAD import Vector from pivy import coin -from DraftGui import todo class Tracker: "A generic Draft Tracker, to be used by other specific trackers" @@ -52,9 +51,11 @@ class Tracker: self.switch.addChild(node) self.switch.whichChild = -1 self.Visible = False + from DraftGui import todo todo.delay(self._insertSwitch, self.switch) def finalize(self): + from DraftGui import todo todo.delay(self._removeSwitch, self.switch) self.switch = None @@ -156,13 +157,21 @@ class lineTracker(Tracker): class rectangleTracker(Tracker): "A Rectangle tracker, used by the rectangle tool" - def __init__(self,dotted=False,scolor=None,swidth=None): + def __init__(self,dotted=False,scolor=None,swidth=None,face=False): self.origin = Vector(0,0,0) line = coin.SoLineSet() line.numVertices.setValue(5) self.coords = coin.SoCoordinate3() # this is the coordinate self.coords.point.setValues(0,50,[[0,0,0],[2,0,0],[2,2,0],[0,2,0],[0,0,0]]) - Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line]) + if face: + m1 = coin.SoMaterial() + m1.transparency.setValue(0.5) + m1.diffuseColor.setValue([0.5,0.5,1.0]) + f = coin.SoIndexedFaceSet() + f.coordIndex.setValues([0,1,2,3]) + Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line,m1,f]) + else: + Tracker.__init__(self,dotted,scolor,swidth,[self.coords,line]) self.u = FreeCAD.DraftWorkingPlane.u self.v = FreeCAD.DraftWorkingPlane.v @@ -783,3 +792,57 @@ class radiusTracker(Tracker): self.trans.translation.setValue([arg2.x,arg2.y,arg2.z]) else: self.sphere.radius.setValue(arg2) + +class archDimTracker(Tracker): + "A wrapper around a Sketcher dim" + def __init__(self,p1=FreeCAD.Vector(0,0,0),p2=FreeCAD.Vector(1,0,0),mode=1): + import SketcherGui + self.dimnode = coin.SoType.fromName("SoDatumLabel").createInstance() + p1node = coin.SbVec3f([p1.x,p1.y,p1.z]) + p2node = coin.SbVec3f([p2.x,p2.y,p2.z]) + self.dimnode.pnts.setValues([p1node,p2node]) + self.dimnode.lineWidth = 1 + color = FreeCADGui.draftToolBar.getDefaultColor("snap") + self.dimnode.textColor.setValue(coin.SbVec3f(color)) + self.setString() + self.setMode(mode) + Tracker.__init__(self,children=[self.dimnode]) + + def setString(self,text=None): + "sets the dim string to the given value or auto value" + self.dimnode.param1.setValue(.5) + p1 = Vector(self.dimnode.pnts.getValues()[0].getValue()) + p2 = Vector(self.dimnode.pnts.getValues()[-1].getValue()) + m = self.dimnode.datumtype.getValue() + if m == 2: + self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(1,0,0))).Length + elif m == 3: + self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(0,1,0))).Length + else: + self.Distance = (p2.sub(p1)).Length + if not text: + text = Draft.getParam("dimPrecision") + text = "%."+str(text)+"f" + text = (text % self.Distance) + self.dimnode.string.setValue(text) + + def setMode(self,mode=1): + """sets the mode: 0 = without lines (a simple mark), 1 = + aligned (default), 2 = horizontal, 3 = vertical.""" + self.dimnode.datumtype.setValue(mode) + + def p1(self,point=None): + "sets or gets the first point of the dim" + if point: + self.dimnode.pnts.set1Value(0,point.x,point.y,point.z) + self.setString() + else: + return Vector(self.dimnode.pnts.getValues()[0].getValue()) + + def p2(self,point=None): + "sets or gets the second point of the dim" + if point: + self.dimnode.pnts.set1Value(1,point.x,point.y,point.z) + self.setString() + else: + return Vector(self.dimnode.pnts.getValues()[-1].getValue()) diff --git a/src/Mod/Draft/Draft_rc.py b/src/Mod/Draft/Draft_rc.py index b521dbd33..7375cb072 100644 --- a/src/Mod/Draft/Draft_rc.py +++ b/src/Mod/Draft/Draft_rc.py @@ -2,43 +2,121 @@ # Resource object code # -# Created: Mon Apr 15 12:19:38 2013 -# by: The Resource Compiler for PyQt (Qt v4.8.1) +# Created: Sat Jul 6 16:32:11 2013 +# by: The Resource Compiler for PyQt (Qt v4.8.4) # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore qt_resource_data = "\ -\x00\x00\x01\x60\ +\x00\x00\x0a\xed\ \x3c\ -\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x22\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\x69\x74\x73\x3d\ -\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\x31\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x2e\x31\x22\x3e\ -\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\ -\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x30\ -\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\ -\x68\x3a\x2e\x30\x30\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x69\x64\x3d\x22\x67\x33\x33\x38\x39\x22\x3e\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x30\x2c\x30\x2e\x30\x35\ -\x20\x6c\x2e\x31\x32\x2c\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x33\x39\x31\ -\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\ -\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\x20\ -\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ -\x00\x00\x01\xaa\ -\x3c\ -\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x63\x72\x6f\x73\x73\x22\x0a\x20\x20\x20\ +\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\ +\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\ +\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\ +\x6e\x6f\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\ +\x6c\x6e\x73\x3a\x64\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\ +\x75\x72\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\ +\x6e\x74\x73\x2f\x31\x2e\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\ +\x6e\x73\x3a\x63\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\ +\x65\x61\x74\x69\x76\x65\x63\x6f\x6d\x6d\x6f\x6e\x73\x2e\x6f\x72\ +\x67\x2f\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\ +\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\ +\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\x2f\x32\ +\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\x73\x23\ +\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x73\x76\x67\x3d\x22\ +\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\ +\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\ +\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\ +\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\ +\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x73\x6f\x64\x69\x70\ +\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x73\x6f\x64\x69\ +\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\x66\x6f\x72\x67\x65\ +\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\x64\x69\x70\x6f\x64\ +\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\ +\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\x22\x68\x74\x74\x70\ +\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\ +\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x73\x2f\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x69\x64\x3d\x22\ +\x73\x76\x67\x33\x34\x34\x38\x22\x0a\x20\x20\x20\x76\x65\x72\x73\ +\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x0a\x20\x20\x20\x69\x6e\x6b\ +\x73\x63\x61\x70\x65\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x30\ +\x2e\x34\x38\x2e\x33\x2e\x31\x20\x72\x39\x38\x38\x36\x22\x0a\x20\ +\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x36\x34\x22\x0a\x20\x20\x20\ +\x68\x65\x69\x67\x68\x74\x3d\x22\x36\x34\x22\x0a\x20\x20\x20\x73\ +\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\x3d\ +\x22\x6c\x69\x6e\x65\x2e\x73\x76\x67\x22\x3e\x0a\x20\x20\x3c\x6d\ +\x65\x74\x61\x64\x61\x74\x61\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\ +\x22\x6d\x65\x74\x61\x64\x61\x74\x61\x33\x34\x35\x37\x22\x3e\x0a\ +\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\x52\x44\x46\x3e\x0a\x20\x20\ +\x20\x20\x20\x20\x3c\x63\x63\x3a\x57\x6f\x72\x6b\x0a\x20\x20\x20\ +\x20\x20\x20\x20\x20\x20\x72\x64\x66\x3a\x61\x62\x6f\x75\x74\x3d\ +\x22\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\ +\x66\x6f\x72\x6d\x61\x74\x3e\x69\x6d\x61\x67\x65\x2f\x73\x76\x67\ +\x2b\x78\x6d\x6c\x3c\x2f\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\ +\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x74\x79\x70\ +\x65\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\ +\x3a\x72\x65\x73\x6f\x75\x72\x63\x65\x3d\x22\x68\x74\x74\x70\x3a\ +\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x64\x63\ +\x6d\x69\x74\x79\x70\x65\x2f\x53\x74\x69\x6c\x6c\x49\x6d\x61\x67\ +\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\ +\x3a\x57\x6f\x72\x6b\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\ +\x3a\x52\x44\x46\x3e\x0a\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\ +\x74\x61\x3e\x0a\x20\x20\x3c\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\ +\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\x0a\x20\x20\x20\x20\x20\x70\ +\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x66\x66\x66\x66\x66\ +\x66\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x63\x6f\ +\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\x36\x36\x36\x22\x0a\x20\x20\ +\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x6f\x70\x61\x63\x69\x74\x79\ +\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x6f\x62\x6a\x65\x63\x74\ +\x74\x6f\x6c\x65\x72\x61\x6e\x63\x65\x3d\x22\x31\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x67\x72\x69\x64\x74\x6f\x6c\x65\x72\x61\x6e\x63\ +\x65\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x20\x20\x67\x75\x69\x64\ +\x65\x74\x6f\x6c\x65\x72\x61\x6e\x63\x65\x3d\x22\x31\x30\x22\x0a\ +\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\ +\x67\x65\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x30\x22\x0a\x20\x20\ +\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x70\x61\x67\x65\ +\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\x22\x0a\x20\x20\x20\x20\x20\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\ +\x77\x69\x64\x74\x68\x3d\x22\x31\x39\x32\x30\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\ +\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\x22\x31\x30\x35\x37\x22\x0a\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6e\x61\x6d\x65\x64\x76\x69\ +\x65\x77\x33\x34\x35\x35\x22\x0a\x20\x20\x20\x20\x20\x73\x68\x6f\ +\x77\x67\x72\x69\x64\x3d\x22\x74\x72\x75\x65\x22\x0a\x20\x20\x20\ +\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x7a\x6f\x6f\x6d\x3d\ +\x22\x33\x2e\x35\x38\x38\x31\x31\x38\x33\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x78\x3d\x22\x35\x30\ +\x2e\x39\x31\x39\x32\x36\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\ +\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\x33\x32\x2e\x33\x39\ +\x33\x31\x34\x38\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x78\x3d\x22\x30\x22\ +\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\ +\x69\x6e\x64\x6f\x77\x2d\x79\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\ +\x2d\x6d\x61\x78\x69\x6d\x69\x7a\x65\x64\x3d\x22\x31\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x75\x72\ +\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\x72\x3d\x22\x73\x76\x67\x33\ +\x34\x34\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x3a\x67\x72\x69\x64\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x74\x79\x70\x65\x3d\x22\x78\x79\x67\x72\x69\x64\x22\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x67\x72\x69\x64\x33\x34\x35\ +\x39\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x65\x6d\x70\x73\x70\x61\ +\x63\x69\x6e\x67\x3d\x22\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x76\x69\x73\x69\x62\x6c\x65\x3d\x22\x74\x72\x75\x65\x22\x0a\x20\ +\x20\x20\x20\x20\x20\x20\x65\x6e\x61\x62\x6c\x65\x64\x3d\x22\x74\ +\x72\x75\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x6e\x61\x70\ +\x76\x69\x73\x69\x62\x6c\x65\x67\x72\x69\x64\x6c\x69\x6e\x65\x73\ +\x6f\x6e\x6c\x79\x3d\x22\x74\x72\x75\x65\x22\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x73\x70\x61\x63\x69\x6e\x67\x78\x3d\x22\x38\x70\x78\ +\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x73\x70\x61\x63\x69\x6e\x67\ +\x79\x3d\x22\x38\x70\x78\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x2f\x73\ +\x6f\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\ +\x77\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\ +\x69\x64\x3d\x22\x64\x65\x66\x73\x33\x34\x35\x30\x22\x3e\x0a\x20\ +\x20\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x69\x64\x3d\x22\x6c\x69\x6e\x65\x22\x0a\x20\x20\x20\ \x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\x69\x74\x73\ \x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\x65\ \x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\x22\x0a\x20\ @@ -50,217 +128,370 @@ qt_resource_data = "\ \x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\ \x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\ \x74\x68\x3a\x2e\x30\x30\x35\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x67\x33\x33\x38\x32\x22\x3e\x0a\x20\x20\ +\x20\x20\x69\x64\x3d\x22\x67\x33\x33\x38\x39\x22\x3e\x0a\x20\x20\ \x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x30\x2c\x30\x20\x6c\ -\x2e\x31\x32\x2c\x2e\x31\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x33\x38\x34\ -\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\ -\x74\x68\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x64\x3d\ -\x22\x4d\x2e\x31\x32\x2c\x30\x20\x6c\x2d\x2e\x31\x32\x2c\x2e\x31\ -\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\ -\x3d\x22\x70\x61\x74\x68\x33\x33\x38\x36\x22\x20\x2f\x3e\x0a\x20\ -\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\ -\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\ -\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ -\x00\x00\x01\xae\ -\x3c\ -\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x20\x20\x20\ -\x20\x0a\x20\x20\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x73\x71\x75\x61\x72\x65\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\ -\x55\x6e\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\ -\x4f\x6e\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\ -\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\ -\x31\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\ -\x3d\x22\x2e\x31\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\ -\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\ -\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\ -\x65\x2d\x77\x69\x64\x74\x68\x3a\x2e\x30\x30\x35\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x67\x33\x33\x39\x34\ -\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\ -\x30\x2c\x30\x2e\x30\x35\x20\x6c\x2e\x31\x32\x2c\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\ -\x74\x68\x33\x33\x39\x36\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x64\x3d\x22\x4d\x30\x2e\x30\x35\x2c\x30\x20\x6c\ -\x30\x2c\x2e\x31\x32\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x33\x39\x38\x22\x20\ -\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\x0a\x20\x20\ -\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\x20\x20\x3c\ -\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ -\x00\x00\x01\x61\ -\x3c\ -\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x73\x69\x6d\x70\x6c\x65\x22\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\x69\x74\ -\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\x55\x73\ -\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\x31\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x2e\x31\ -\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\ -\x3a\x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\ -\x30\x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\ -\x64\x74\x68\x3a\x2e\x30\x30\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x67\x33\x33\x37\x37\x22\x3e\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x30\x2c\x30\x20\ -\x6c\x2e\x31\x32\x2c\x2e\x31\x32\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x33\x37\ -\x39\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\ +\x20\x20\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x30\x2c\x30\x2e\x30\ +\x35\x20\x6c\x2e\x31\x32\x2c\x30\x22\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x33\x39\ +\x31\x22\x20\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x67\x3e\ \x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\x65\x72\x6e\x3e\x0a\ -\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\ -\ -\x00\x00\x08\xd6\ -\x3c\ -\x73\x76\x67\x3e\x0a\x20\x20\x3c\x64\x65\x66\x73\x3e\x0a\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x74\x65\x72\x6e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x69\x64\x3d\x22\x63\x6f\x6e\x63\x72\x65\x74\x65\x22\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x70\x61\x74\x74\x65\x72\x6e\x55\x6e\ -\x69\x74\x73\x3d\x22\x75\x73\x65\x72\x53\x70\x61\x63\x65\x4f\x6e\ -\x55\x73\x65\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x78\x3d\x22\x30\ -\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x77\x69\x64\x74\x68\x3d\x22\x2e\x35\x22\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ -\x2e\x35\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x67\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ -\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x20\x73\x74\x72\x6f\x6b\x65\x3a\ -\x23\x30\x30\x30\x30\x30\x30\x3b\x20\x73\x74\x72\x6f\x6b\x65\x2d\ -\x77\x69\x64\x74\x68\x3a\x2e\x35\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x3d\x22\x73\x63\ -\x61\x6c\x65\x28\x2e\x30\x31\x29\x22\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x20\x69\x64\x3d\x22\x67\x33\x34\x30\x31\x22\x3e\x0a\x20\ -\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\ -\x6d\x20\x35\x2e\x33\x35\x37\x31\x34\x32\x38\x2c\x33\x2e\x35\x32\ -\x32\x38\x39\x34\x36\x20\x61\x20\x31\x2e\x33\x33\x39\x32\x38\x35\ -\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\ -\x31\x20\x2d\x32\x2e\x36\x37\x38\x35\x37\x31\x35\x2c\x30\x20\x31\ -\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\x32\x38\ -\x35\x37\x20\x30\x20\x31\x20\x31\x20\x32\x2e\x36\x37\x38\x35\x37\ -\x31\x35\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\ -\x31\x30\x37\x31\x34\x33\x2c\x36\x2e\x38\x32\x36\x34\x36\x36\x31\ -\x20\x61\x20\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x2c\x33\x2e\x32\ -\x31\x34\x32\x38\x35\x36\x20\x30\x20\x31\x20\x31\x20\x2d\x36\x2e\ -\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\x33\x2e\x32\x31\x34\x32\ -\x38\x35\x36\x2c\x33\x2e\x32\x31\x34\x32\x38\x35\x36\x20\x30\x20\ -\x31\x20\x31\x20\x36\x2e\x34\x32\x38\x35\x37\x31\x31\x2c\x30\x20\ -\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\ -\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x34\x2e\x38\x32\x31\x34\x32\ -\x38\x2c\x33\x30\x2e\x39\x33\x33\x36\x30\x39\x20\x61\x20\x30\x2e\ -\x37\x31\x34\x32\x38\x35\x37\x33\x2c\x30\x2e\x37\x31\x34\x32\x38\ -\x35\x37\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x34\x32\x38\ -\x35\x37\x31\x2c\x30\x20\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\ -\x2c\x30\x2e\x37\x31\x34\x32\x38\x35\x37\x33\x20\x30\x20\x31\x20\ -\x31\x20\x31\x2e\x34\x32\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\ -\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\ -\x64\x3d\x22\x6d\x20\x32\x38\x2e\x35\x37\x31\x34\x32\x38\x2c\x31\ -\x36\x2e\x32\x30\x31\x34\x36\x36\x20\x61\x20\x32\x2e\x32\x33\x32\ -\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x20\x30\ -\x20\x31\x20\x31\x20\x2d\x34\x2e\x34\x36\x34\x32\x38\x36\x2c\x30\ -\x20\x32\x2e\x32\x33\x32\x31\x34\x32\x39\x2c\x32\x2e\x32\x33\x32\ -\x31\x34\x32\x39\x20\x30\x20\x31\x20\x31\x20\x34\x2e\x34\x36\x34\ -\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\x2e\ -\x32\x35\x30\x30\x30\x30\x32\x2c\x32\x30\x2e\x30\x38\x35\x33\x39\ -\x34\x20\x61\x20\x30\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x2c\x30\ -\x2e\x34\x39\x31\x30\x37\x31\x34\x33\x20\x30\x20\x31\x20\x31\x20\ -\x2d\x30\x2e\x39\x38\x32\x31\x34\x32\x39\x2c\x30\x20\x30\x2e\x34\ -\x39\x31\x30\x37\x31\x34\x33\x2c\x30\x2e\x34\x39\x31\x30\x37\x31\ -\x34\x33\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x39\x38\x32\x31\x34\ -\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x33\x34\x2e\ -\x37\x33\x32\x31\x34\x34\x2c\x32\x37\x2e\x35\x32\x37\x37\x32\x35\ -\x20\x61\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x31\x33\x2c\x30\x2e\ -\x34\x33\x33\x34\x30\x31\x39\x37\x20\x30\x20\x31\x20\x31\x20\x2d\ -\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\x30\x20\x30\x2e\x32\x36\x37\ -\x38\x35\x37\x31\x33\x2c\x30\x2e\x34\x33\x33\x34\x30\x31\x39\x37\ -\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x2c\ -\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\ -\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x31\x37\x38\ -\x35\x37\x32\x2c\x39\x2e\x33\x37\x31\x31\x30\x38\x31\x20\x61\x20\ -\x30\x2e\x31\x33\x33\x39\x32\x38\x35\x37\x2c\x30\x2e\x32\x32\x33\ -\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x32\ -\x36\x37\x38\x35\x37\x2c\x30\x20\x30\x2e\x31\x33\x33\x39\x32\x38\ -\x35\x37\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\ -\x31\x20\x31\x20\x30\x2e\x32\x36\x37\x38\x35\x37\x2c\x30\x20\x7a\ -\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\ -\x68\x20\x64\x3d\x22\x6d\x20\x34\x30\x2e\x39\x38\x32\x31\x34\x34\ -\x2c\x33\x38\x2e\x37\x34\x36\x31\x30\x39\x20\x61\x20\x30\x2e\x32\ -\x32\x33\x32\x31\x34\x32\x38\x2c\x30\x2e\x32\x32\x33\x32\x31\x34\ -\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x34\x34\x36\x34\ -\x32\x38\x2c\x30\x20\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x2c\ -\x30\x2e\x32\x32\x33\x32\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\ -\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x2c\x30\x20\x7a\x22\x2f\x3e\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ -\x3d\x22\x6d\x20\x33\x31\x2e\x31\x36\x30\x37\x31\x35\x2c\x34\x30\ -\x2e\x35\x37\x36\x34\x36\x36\x20\x61\x20\x33\x2e\x39\x32\x38\x35\ -\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x20\x30\x20\ -\x31\x20\x31\x20\x2d\x37\x2e\x38\x35\x37\x31\x34\x33\x2c\x30\x20\ -\x33\x2e\x39\x32\x38\x35\x37\x31\x35\x2c\x33\x2e\x39\x32\x38\x35\ -\x37\x31\x35\x20\x30\x20\x31\x20\x31\x20\x37\x2e\x38\x35\x37\x31\ -\x34\x33\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\ -\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x32\x32\x2e\ -\x32\x33\x32\x31\x34\x33\x2c\x33\x38\x2e\x32\x35\x35\x30\x33\x35\ -\x20\x61\x20\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x2c\x31\x2e\x36\ -\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\x20\x31\x20\x2d\x33\x2e\ -\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x31\x2e\x36\x30\x37\x31\x34\ -\x32\x38\x2c\x31\x2e\x36\x30\x37\x31\x34\x32\x38\x20\x30\x20\x31\ -\x20\x31\x20\x33\x2e\x32\x31\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\ -\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\ -\x20\x64\x3d\x22\x6d\x20\x34\x31\x2e\x39\x36\x34\x32\x38\x37\x2c\ -\x31\x31\x2e\x36\x34\x37\x38\x39\x35\x20\x61\x20\x30\x2e\x38\x39\ -\x32\x38\x35\x37\x31\x33\x2c\x30\x2e\x38\x39\x32\x38\x35\x37\x31\ -\x33\x20\x30\x20\x31\x20\x31\x20\x2d\x31\x2e\x37\x38\x35\x37\x31\ -\x34\x2c\x30\x20\x30\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x2c\x30\ -\x2e\x38\x39\x32\x38\x35\x37\x31\x33\x20\x30\x20\x31\x20\x31\x20\ -\x31\x2e\x37\x38\x35\x37\x31\x34\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\ -\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\ -\x22\x6d\x20\x33\x36\x2e\x30\x37\x31\x34\x33\x2c\x32\x35\x2e\x37\ -\x35\x35\x30\x33\x37\x20\x61\x20\x30\x2e\x33\x35\x37\x31\x34\x32\ -\x38\x37\x2c\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x20\x30\x20\ -\x31\x20\x31\x20\x2d\x30\x2e\x37\x31\x34\x32\x38\x36\x2c\x30\x20\ -\x30\x2e\x33\x35\x37\x31\x34\x32\x38\x37\x2c\x30\x2e\x33\x35\x37\ -\x31\x34\x32\x38\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\x37\x31\ -\x34\x32\x38\x36\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\ -\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x36\ -\x2e\x32\x35\x2c\x34\x32\x2e\x39\x38\x37\x31\x37\x39\x20\x61\x20\ -\x30\x2e\x36\x32\x35\x2c\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\ -\x31\x20\x2d\x31\x2e\x32\x35\x2c\x30\x20\x30\x2e\x36\x32\x35\x2c\ -\x30\x2e\x36\x32\x35\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x32\x35\ -\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\ -\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\x20\x31\x37\x2e\x33\x32\ -\x31\x34\x32\x38\x2c\x31\x39\x2e\x39\x35\x31\x34\x36\x36\x20\x61\ -\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x2c\x31\x2e\x33\x33\x39\ -\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x2d\x32\x2e\x36\x37\ -\x38\x35\x37\x31\x2c\x30\x20\x31\x2e\x33\x33\x39\x32\x38\x35\x37\ -\x2c\x31\x2e\x33\x33\x39\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\ -\x20\x32\x2e\x36\x37\x38\x35\x37\x31\x2c\x30\x20\x7a\x22\x2f\x3e\ -\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\ -\x3d\x22\x6d\x20\x31\x31\x2e\x39\x36\x34\x32\x38\x36\x2c\x31\x38\ -\x2e\x31\x36\x35\x37\x35\x32\x20\x61\x20\x30\x2e\x34\x34\x36\x34\ -\x32\x38\x35\x37\x2c\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x20\ -\x30\x20\x31\x20\x31\x20\x2d\x30\x2e\x38\x39\x32\x38\x35\x37\x2c\ -\x30\x20\x30\x2e\x34\x34\x36\x34\x32\x38\x35\x37\x2c\x30\x2e\x34\ -\x34\x36\x34\x32\x38\x35\x37\x20\x30\x20\x31\x20\x31\x20\x30\x2e\ -\x38\x39\x32\x38\x35\x37\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\ -\x20\x20\x20\x20\x20\x20\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x6d\ -\x20\x31\x36\x2e\x36\x30\x37\x31\x34\x33\x2c\x37\x2e\x38\x39\x37\ -\x38\x39\x34\x39\x20\x61\x20\x30\x2e\x35\x33\x35\x37\x31\x34\x32\ -\x37\x2c\x30\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x20\x30\x20\x31\ -\x20\x31\x20\x2d\x31\x2e\x30\x37\x31\x34\x32\x39\x2c\x30\x20\x30\ -\x2e\x35\x33\x35\x37\x31\x34\x32\x37\x2c\x30\x2e\x35\x33\x35\x37\ -\x31\x34\x32\x37\x20\x30\x20\x31\x20\x31\x20\x31\x2e\x30\x37\x31\ -\x34\x32\x39\x2c\x30\x20\x7a\x22\x2f\x3e\x0a\x20\x20\x20\x20\x20\ -\x20\x3c\x2f\x67\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x70\x61\x74\x74\ -\x65\x72\x6e\x3e\x0a\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x3c\ -\x2f\x73\x76\x67\x3e\ +\x20\x20\x3c\x2f\x64\x65\x66\x73\x3e\x0a\x20\x20\x3c\x70\x61\x74\ +\x68\x0a\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ +\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\ +\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\ +\x64\x74\x68\x3a\x31\x70\x78\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\ +\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\ +\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x6d\x69\x74\x65\ +\x72\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\ +\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x64\x3d\x22\x6d\x20\x30\x2c\ +\x35\x36\x20\x36\x34\x2c\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x64\ +\x3d\x22\x70\x61\x74\x68\x33\x34\x36\x31\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6e\x6e\x65\x63\ +\x74\x6f\x72\x2d\x63\x75\x72\x76\x61\x74\x75\x72\x65\x3d\x22\x30\ +\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\ +\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\ +\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\ +\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x31\ +\x70\x78\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\ +\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\ +\x6e\x65\x6a\x6f\x69\x6e\x3a\x6d\x69\x74\x65\x72\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\ +\x20\x20\x20\x20\x64\x3d\x22\x6d\x20\x30\x2c\x34\x30\x20\x36\x34\ +\x2c\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\ +\x68\x33\x34\x36\x33\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\ +\x63\x61\x70\x65\x3a\x63\x6f\x6e\x6e\x65\x63\x74\x6f\x72\x2d\x63\ +\x75\x72\x76\x61\x74\x75\x72\x65\x3d\x22\x30\x22\x20\x2f\x3e\x0a\ +\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\x73\x74\x79\ +\x6c\x65\x3d\x22\x66\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\ +\x72\x6f\x6b\x65\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x31\x70\x78\x3b\x73\x74\ +\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\ +\x74\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\ +\x6e\x3a\x6d\x69\x74\x65\x72\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\ +\x70\x61\x63\x69\x74\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x64\ +\x3d\x22\x6d\x20\x30\x2c\x32\x34\x20\x36\x34\x2c\x30\x22\x0a\x20\ +\x20\x20\x20\x20\x69\x64\x3d\x22\x70\x61\x74\x68\x33\x34\x36\x35\ +\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\ +\x63\x6f\x6e\x6e\x65\x63\x74\x6f\x72\x2d\x63\x75\x72\x76\x61\x74\ +\x75\x72\x65\x3d\x22\x30\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x70\x61\ +\x74\x68\x0a\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\ +\x69\x6c\x6c\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x3a\ +\x23\x30\x30\x30\x30\x30\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\ +\x69\x64\x74\x68\x3a\x31\x70\x78\x3b\x73\x74\x72\x6f\x6b\x65\x2d\ +\x6c\x69\x6e\x65\x63\x61\x70\x3a\x62\x75\x74\x74\x3b\x73\x74\x72\ +\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\x6d\x69\x74\ +\x65\x72\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\ +\x79\x3a\x31\x22\x0a\x20\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x30\ +\x2c\x38\x20\x36\x34\x2c\x38\x22\x0a\x20\x20\x20\x20\x20\x69\x64\ +\x3d\x22\x70\x61\x74\x68\x33\x34\x36\x37\x22\x0a\x20\x20\x20\x20\ +\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x6f\x6e\x6e\x65\x63\ +\x74\x6f\x72\x2d\x63\x75\x72\x76\x61\x74\x75\x72\x65\x3d\x22\x30\ +\x22\x20\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ +\x00\x00\x04\x2e\ +\x00\ +\x00\x13\x35\x78\x9c\xd5\x58\xcb\x6e\xe3\x36\x14\xdd\xe7\x2b\x04\ +\x66\xd9\x58\x6f\x2b\x92\xc6\xf2\x6c\x82\x01\xba\x28\x0a\x74\x26\ +\xe8\x9a\x96\x68\x5b\x13\x89\x14\x48\xfa\x35\x5f\xdf\x4b\x8a\x7a\ +\xd8\xd2\xa0\xed\x6a\x6c\x01\x01\xc2\x73\x0f\x2f\x79\xee\x4b\x4a\ +\x56\x9f\xcf\x75\x65\x1d\x09\x17\x25\xa3\x19\xf2\x6c\x17\x59\x84\ +\xe6\xac\x28\xe9\x2e\x43\xef\xdf\xbe\x2c\x62\x64\x09\x89\x69\x81\ +\x2b\x46\x49\x86\x28\x43\x9f\xd7\x4f\x2b\x71\xdc\x3d\x59\x96\x05\ +\x9b\xa9\x48\x8b\x3c\x43\x7b\x29\x9b\xd4\x71\x9a\x03\xaf\x6c\xc6\ +\x77\x4e\x91\x3b\xa4\x22\x35\xa1\x52\x38\x9e\xed\x39\x68\xa0\xe7\ +\x03\x3d\xe7\x04\xcb\xf2\x48\x72\x56\xd7\x8c\x0a\xbd\x93\x8a\xe7\ +\x11\x99\x17\xdb\x9e\x7d\x3a\x9d\xec\x53\xa0\x49\x5e\x92\x24\x8e\ +\xeb\x3b\xbe\xbf\x00\xc6\x42\x5c\xa8\xc4\xe7\xc5\xf5\x56\xb8\xe3\ +\xdc\x56\xdf\x75\x5d\x07\x6c\x03\xf3\xbf\xb1\x52\x01\x51\x69\xe0\ +\xa7\xa7\x77\x80\x2d\xd8\x81\xe7\x64\x0b\xfb\x88\x4d\x89\x74\xde\ +\xbe\xbd\xf5\xc6\x85\x6b\x17\xb2\x18\xb9\x29\xe9\x87\xc8\x71\x43\ +\xae\x4e\xed\xc0\x36\x02\xb8\x26\xa2\xc1\x39\x11\x4e\x87\xeb\xfd\ +\x65\x91\x21\xb8\x52\xb0\x0c\x7d\xbd\x1e\xa5\xcd\x6b\x09\x86\x9d\ +\xf6\x16\xd7\x0e\x63\x3b\xb0\x3d\x8b\x27\x71\x1c\x69\xd2\xa9\x2c\ +\xe4\x3e\x43\x51\xa8\x57\x7b\x52\xee\xf6\xb2\x5f\x76\xb7\x4e\x0b\ +\x96\xab\x6b\x64\x28\xe7\x4c\x08\x5b\x45\x62\x0d\x84\x55\x4d\x24\ +\x2e\xb0\xc4\x8a\xdc\xde\xa8\x43\x82\xe5\xd2\xd7\x1c\x60\x41\x4e\ +\xd2\xbf\xde\xbe\xb4\x2b\x58\xe7\x79\xfa\x37\xe3\x1f\x66\x09\x8f\ +\x22\xe0\x0d\x3b\xc0\xc9\x68\xdd\xc3\xab\x22\x4f\x21\x8a\x35\x96\ +\xeb\xb2\xc6\x3b\xa2\x12\xf0\x1b\x44\x6d\xe5\x0c\x86\x2b\xb2\xbc\ +\x34\x64\x70\xda\xba\xe5\xa4\x4d\xc7\x6c\x4d\x16\x79\x5d\xaa\x4d\ +\xce\x57\x59\x56\xd5\xef\xea\x10\x64\x39\xfd\x3d\x1d\x73\x51\x23\ +\xc3\x19\xe9\x58\x39\x9d\x50\xbd\xea\x03\xa5\xa2\x54\x1c\x4b\x72\ +\x6a\x7d\x34\xe0\x31\x67\x15\xe3\x19\x7a\xde\xea\x07\xb5\x86\x0d\ +\xe3\x05\xe1\x9d\x29\xd2\xcf\x95\x89\x41\xc6\xe1\x6e\x90\x4d\x03\ +\xb3\xcd\x77\x92\x4b\xc9\x2a\xc2\x31\x55\x7a\x3c\xd7\x58\x76\x1c\ +\x72\x38\x87\x1f\xca\x82\xcc\x19\xfa\xc2\x50\xd7\xeb\x0f\x9a\xb5\ +\x8a\x3d\x2e\xd8\x29\x43\xfe\xad\xf1\x54\x52\x30\x2c\x4c\xf9\x78\ +\x89\x3f\xd9\x6e\x18\x5d\x49\x79\xee\xf2\x15\x0d\x75\xd2\x07\x0a\ +\x0a\xa5\xdb\x2a\xf6\xec\xa4\xc4\x64\x48\xf2\x03\xb9\xf5\xf7\x83\ +\xb1\x3a\x43\x4b\xdb\x7d\x0d\x83\x68\xb9\xbc\x35\xe7\xe7\x0c\x41\ +\x77\x25\x61\xb8\x7c\x85\x71\x30\x31\x83\xc2\xc0\xb7\xfd\x20\xf0\ +\xe3\x89\xd1\x5c\xf5\x3c\x13\x04\x63\x9a\x8b\x8f\x31\xd5\xf8\x5c\ +\xd6\xe5\x0f\x52\x0c\xb9\x1a\x4e\x3d\x70\x0e\x43\x6f\x51\xe1\x0b\ +\xe1\x43\xbb\x9a\x82\xea\x69\x4a\x74\x57\xb8\xaa\x1e\x33\x74\xbe\ +\x28\x0c\x75\xa0\x8a\x89\x02\x82\x28\xf2\x7b\x90\xd4\x8d\x9a\x0b\ +\x7a\x36\x2f\x7b\xf4\x58\x8a\x72\x53\x91\xab\x18\x02\x97\x62\x00\ +\x8b\x1b\x54\x50\xdc\x18\xbe\xf2\x5e\x95\x14\xba\x85\x56\x97\x5b\ +\x5a\x7b\x0a\x84\x27\x6e\xce\xb7\xe8\xa5\x45\xdb\xb6\x59\x39\xd3\ +\x4e\xd0\x78\x41\xb6\x62\x48\xbe\x5a\x41\x20\xc2\x2e\x10\x0d\x96\ +\x92\x70\x3a\x56\xab\x07\x4d\x7f\x96\x21\xbc\xd3\x52\xc2\x7c\x3e\ +\x08\xc2\xbf\xaa\x89\xf8\x27\x7d\x17\xc3\x35\x47\xe9\xb3\xac\xcb\ +\x78\x61\xaa\xd4\xf6\x7a\xa4\xab\x4a\x80\xfa\x76\xdf\x0d\xb3\x43\ +\xc8\x8b\x0a\xe1\x16\xa6\x42\x4a\xe1\x4d\xf7\x09\x10\xce\x3e\x48\ +\xfa\xec\xea\xa7\x5b\xb7\xf5\x9f\xda\xae\x3b\x24\xc0\x64\x2b\x08\ +\x62\x7f\x3c\xcc\x40\xc2\x7e\x3c\x9c\x80\xf3\x87\xfb\xe2\x5a\x95\ +\xed\xf9\x2f\xf0\x83\xc6\x46\xe5\x41\x6d\x00\x27\xe1\x68\x22\xfd\ +\xc4\x8d\xf2\x00\x8e\x16\xff\xe2\x29\xba\x9a\x6d\xbb\x6e\xaa\x99\ +\xd8\xb6\xe9\x53\x99\xd1\xbf\x0d\xe7\x4c\x62\x71\x13\x8a\xab\x48\ +\x78\xcd\xb9\x03\x54\x35\x41\x75\xa7\x9b\x83\x94\x63\xec\x3b\x2b\ +\x69\x0a\x73\x97\xf0\x0e\x35\x23\x28\xed\xd2\xa3\x34\x59\x51\x08\ +\x9a\xdc\x17\xf3\x26\x1a\x84\xf8\x49\x12\x4f\xda\x8c\x51\x38\x4b\ +\x32\xbe\x80\x86\x3b\x62\x79\xe0\x44\xa5\xdf\xd4\xe4\x2f\x97\x12\ +\xc6\x5a\x4a\x18\xdf\x48\x09\xe0\xcc\x07\x93\xe2\x45\x90\x11\x95\ +\x1b\x2f\x9a\x8a\x99\xbc\x24\xee\x5c\x4c\xe0\xeb\xbc\x04\xfe\x54\ +\x4a\xf8\x60\x52\x20\x2f\x4a\xca\x5c\x56\xa2\x07\x93\x02\xc5\x15\ +\xf8\x2a\x37\x93\xd6\x07\x31\x8f\xd6\xfa\x20\x26\x8c\xd5\x00\x98\ +\x11\x33\xfd\x1e\xbb\x73\x31\xea\x75\x05\x82\xa6\x52\xa2\x38\x78\ +\x30\x29\xba\x5f\x74\x72\xa6\x5a\xa6\x9f\x96\xf7\xad\x45\x8f\x31\ +\xdd\x35\x53\x2d\xaf\x0f\xa6\x45\xbf\x2a\x67\x5f\x2f\xd1\xf4\xb3\ +\xfd\xce\xb5\xa8\x71\x3c\xdf\xfa\x51\x32\xfd\x4b\xe1\xde\xb5\xfc\ +\x6c\x26\x47\xc9\xa3\xf5\xbe\xfa\x12\x6b\x3f\x64\xa6\x5a\xfe\x4f\ +\xef\xaf\xd4\x3f\x24\xd6\x4f\xff\x00\x3d\xa0\xd3\x03\ +\x00\x00\x03\xd9\ +\x00\ +\x00\x0e\x64\x78\x9c\xd5\x56\xc9\x8e\xdb\x38\x10\xbd\xf7\x57\x08\ +\xec\xe3\xd8\x12\xb5\x58\x92\x15\xcb\xb9\x34\x02\xcc\x21\x18\x60\ +\x92\x46\xce\xb4\x44\xdb\x4a\x4b\xa4\x86\xa4\xbc\xe4\xeb\xa7\xa8\ +\x85\x92\x37\x60\xe6\x16\x0b\x68\xa0\xf9\xea\xb1\x8a\xaf\xb6\xee\ +\xd5\xe7\x53\x55\x5a\x07\x2a\x64\xc1\x59\x8a\x5c\x1b\x23\x8b\xb2\ +\x8c\xe7\x05\xdb\xa5\xe8\xfd\xfb\x97\x79\x8c\x2c\xa9\x08\xcb\x49\ +\xc9\x19\x4d\x11\xe3\xe8\xf3\xfa\x65\x25\x0f\xbb\x17\xcb\xb2\xe0\ +\x32\x93\x49\x9e\xa5\x68\xaf\x54\x9d\x38\x4e\xdd\x88\xd2\xe6\x62\ +\xe7\xe4\x99\x43\x4b\x5a\x51\xa6\xa4\xe3\xda\xae\x83\x46\x7a\x36\ +\xd2\x33\x41\x89\x2a\x0e\x34\xe3\x55\xc5\x99\x6c\x6f\x32\xf9\x3a\ +\x21\x8b\x7c\x6b\xd8\xc7\xe3\xd1\x3e\xfa\x2d\xc9\x5d\x2e\x97\x0e\ +\xf6\x1c\xcf\x9b\x03\x63\x2e\xcf\x4c\x91\xd3\xfc\xf2\x2a\xbc\xf1\ +\xde\x55\x0f\x63\xec\x80\x6d\x64\xfe\x37\x56\x22\x21\x2b\x35\xfc\ +\x18\xfa\x00\xd8\x92\x37\x22\xa3\x5b\xb8\x47\x6d\x46\x95\xf3\xf6\ +\xfd\xcd\x18\xe7\xd8\xce\x55\x3e\x71\x53\xb0\x0f\x99\x91\x9a\x5e\ +\x44\x1d\xc0\x2e\x03\xa4\xa2\xb2\x26\x19\x95\xce\x80\xb7\xf7\x8b\ +\x3c\x45\xf0\x24\x3f\x8a\x83\xf6\x3c\x29\x9b\xdb\x11\x7a\x76\x62\ +\x2c\xd8\x0e\x62\xdb\xb7\x5d\x4b\x2c\xe3\x38\x6c\x49\xc7\x22\x57\ +\xfb\x14\x85\x9d\x8f\x3d\x2d\x76\x7b\x65\x8e\xc3\xab\x93\x9c\x67\ +\xfa\x19\x10\xf0\x9f\x86\x08\x6a\xeb\x54\xac\x81\xb1\xaa\xa8\x22\ +\x39\x51\x44\xb3\xbb\x27\x0d\x88\x1f\x2d\x83\x96\x03\x2c\x28\x4a\ +\xf2\xf7\xdb\x97\xee\x04\xe7\x2c\x4b\x7e\x70\xf1\xd1\x1f\xe1\xd3\ +\x04\xb2\xe1\x0d\x84\x46\x6b\x03\xaf\xf2\x2c\x81\x34\x56\x44\xad\ +\x8b\x8a\xec\xa8\xae\xc0\x1f\x90\xb6\x95\x33\x1a\x2e\xc8\xea\x5c\ +\xd3\xd1\x69\xe7\x56\xd0\xae\x1e\x77\x9b\x32\xcf\xaa\x42\x5f\x72\ +\xbe\xa9\xa2\x2c\xff\xd4\x41\x90\xe5\x98\x77\x3a\xfd\x43\x7b\x19\ +\xce\x44\xc7\xca\x19\x84\xb6\x27\x93\x29\x9d\xa6\xfc\x50\xd0\x63\ +\xe7\xa3\x06\x8f\x19\x2f\xb9\x48\xd1\xeb\xb6\xfd\x50\x67\xd8\x70\ +\x91\x53\x31\x98\xc2\xf6\xbb\x30\x71\x28\x39\xbc\x0d\xca\xd9\xc3\ +\x7c\xf3\x93\x66\x4a\xf1\x92\x0a\xc2\xb4\x1e\x17\xf7\x96\x9d\x80\ +\x22\xde\xc3\x9b\x22\xa7\xf7\x0c\xa6\x33\xf4\xf3\x4c\xa0\xbb\x56\ +\xb9\x27\x39\x3f\xa6\xc8\xbb\x36\x1e\x0b\x06\x86\x79\xdf\x3f\xee\ +\xd2\xbb\xb9\xde\x33\x86\x9e\x72\xf1\x22\x42\x63\x9f\x98\x44\x41\ +\xa3\x0c\xce\xe5\x9e\x1f\xb5\x98\x14\x29\xd1\xd0\x6b\x7f\xbf\x38\ +\xaf\x52\x14\xd9\x5e\x80\xa3\xc8\x0f\xae\xcd\xd9\x29\x45\x7e\x68\ +\x87\x9e\x1f\x45\xf1\x8d\x11\xf4\xf9\xbe\xed\x2f\x22\xdf\x7d\x24\ +\xe5\x74\x27\x05\xbd\xe9\x5e\x76\x7a\x53\x45\x4e\x45\x55\xfc\xa2\ +\xf9\x58\xa9\x31\x6a\x23\x04\xec\xbc\x79\x49\xce\x54\x8c\xd3\xda\ +\xb7\x93\xa1\x69\xc9\x43\xdb\xea\x6e\x4c\xd1\xe9\xac\x31\x34\x80\ +\x3a\x23\x1a\x80\x54\x85\x06\xa4\x55\xad\xd7\x42\xbb\x9a\x17\x06\ +\x3d\x14\xb2\xd8\x94\xf4\x22\x83\xc0\x65\x04\xc0\xfc\x0a\x95\x8c\ +\xd4\x3d\x5f\x7b\x2f\x0b\x06\xb3\xc2\xca\xf3\x35\xad\x8b\x02\xe9\ +\x89\xeb\xd3\x35\x7a\xee\xd0\x6e\x68\x56\xce\xed\x1c\xb4\x78\x4e\ +\xb7\x72\x2c\xbd\x3e\x41\x22\xc2\x21\x11\x35\x51\x8a\x0a\x36\x55\ +\xdb\xed\x19\x13\xac\x67\xbc\xb3\x42\xc1\x7e\x6e\x24\x15\xdf\xf4\ +\x46\xfc\x8b\xbd\xcb\x91\x34\xa9\x9f\x65\x9d\xa7\x87\xbe\x49\x6d\ +\xd7\x20\x43\x53\x02\x64\xa6\x7d\x37\xae\x0e\xa9\xce\x3a\x87\x5b\ +\x58\x0a\x09\x83\xbf\x74\x9f\x00\x11\xfc\x83\x26\xaf\xb8\xfd\x86\ +\x73\xd7\xfe\x89\x8d\xf1\x58\x81\xbe\x5c\xbe\x6f\xf6\xdf\x20\x72\ +\x3f\xdd\x4d\xc0\xf9\x8a\x67\xd8\xc6\x0b\xab\xb4\x5d\x6f\x86\xd1\ +\xd4\xaa\x5d\xe8\x1b\xe0\x25\x9c\x6c\xa4\x47\x7e\xc0\xcb\x0c\x5b\ +\x25\x9e\xd9\xa6\xbb\x6f\xfc\xc4\x17\x9b\x6d\x37\xec\xb4\x3e\xb5\ +\x5d\xf9\x74\x65\xda\xdf\xc6\x28\x37\xa9\xb8\xca\xc4\x45\x22\xdc\ +\xfa\x34\x00\xba\x9b\xa0\xbb\x93\x4d\xa3\xd4\x14\xfb\xc9\x0b\x96\ +\xc0\xd6\xa5\x62\x40\xfb\x05\x94\x0c\xd5\xd1\x7f\x44\x2c\x3c\x5b\ +\x84\x56\x18\x98\xb4\x18\x21\x41\x78\x3b\x66\x9c\x41\x2c\xc5\xc5\ +\x1c\x06\xee\x40\x54\x23\xa8\xae\x7e\xdf\x93\xbf\x81\x94\x00\x3f\ +\x90\xe2\x3f\x9d\x14\x2f\x78\x20\x65\xf1\x54\x52\xbe\x82\x94\x58\ +\x2b\x89\x6f\x95\x44\x4f\xa6\x24\x86\xd1\x8f\x67\x61\x70\xad\x24\ +\x76\x9f\x6d\x52\x3c\xe8\x2c\x28\xcc\x3d\x29\xcf\x36\x29\x01\x7e\ +\x28\xe5\xb9\x26\xa5\xb2\x16\xe1\x43\x29\xff\x67\x54\x56\xfa\xdf\ +\xf7\xf5\xcb\xbf\xca\xa1\x4f\xfa\ +\x00\x00\x03\xdc\ +\x00\ +\x00\x0d\x76\x78\x9c\xd5\x56\x4d\x8f\xe3\x36\x0c\xbd\xcf\xaf\x30\ +\x34\xc7\x4e\xfc\x99\x71\x6c\x37\xce\x5e\x06\x0b\xf4\x50\x14\xe8\ +\xee\xa0\x67\xc5\x52\x12\xed\xd8\x92\x21\x29\x5f\xfb\xeb\x4b\xd9\ +\x92\xed\xc4\x41\xd1\x5b\x3b\x06\x0c\x58\x8f\x4f\xa4\x1e\x49\x31\ +\x59\x7f\xb9\x34\xb5\x77\xa2\x52\x31\xc1\x4b\x14\xf9\x21\xf2\x28\ +\xaf\x04\x61\x7c\x5f\xa2\xf7\xef\x5f\x17\x19\xf2\x94\xc6\x9c\xe0\ +\x5a\x70\x5a\x22\x2e\xd0\x97\xcd\xd3\x5a\x9d\xf6\x4f\x9e\xe7\xc1\ +\x66\xae\x0a\x52\x95\xe8\xa0\x75\x5b\x04\x41\x7b\x94\xb5\x2f\xe4\ +\x3e\x20\x55\x40\x6b\xda\x50\xae\x55\x10\xf9\x51\x80\x46\x7a\x35\ +\xd2\x2b\x49\xb1\x66\x27\x5a\x89\xa6\x11\x5c\x75\x3b\xb9\x7a\x9e\ +\x90\x25\xd9\x0d\xec\xf3\xf9\xec\x9f\x93\x8e\x14\xe5\x79\x1e\x84\ +\x71\x10\xc7\x0b\x60\x2c\xd4\x95\x6b\x7c\x59\xdc\x6e\x85\x33\x3e\ +\xda\x1a\x87\x61\x18\x80\x6d\x64\xfe\x3b\x56\xa1\x20\x2b\x2d\xbc\ +\x03\xdd\x01\xbe\x12\x47\x59\xd1\x1d\xec\xa3\x3e\xa7\x3a\x78\xfb\ +\xfe\x36\x18\x17\xa1\x4f\x34\x99\xb8\x61\xfc\x43\x55\xb8\xa5\x37\ +\x51\x1d\xd8\x67\x00\x37\x54\xb5\xb8\xa2\x2a\x70\x78\xb7\x9f\x91\ +\x12\xc1\x91\xe2\x3c\x7b\xed\xd6\x93\xb2\x45\x3d\xc1\xb2\x8b\xc1\ +\x12\xfa\xcb\xcc\x4f\xfc\xc8\x93\x79\x96\xa5\x1d\xe9\xcc\x88\x3e\ +\x94\x28\x5d\x76\xab\x03\x65\xfb\x83\x1e\x96\xee\xd4\x05\x11\x95\ +\x39\x06\x04\x64\x4d\x5b\x53\xdf\xa4\x62\x03\x8c\x75\x43\x35\x26\ +\x58\x63\xc3\xee\x8f\xe4\x90\x38\xcf\x97\x1d\x07\x58\x50\x94\xe2\ +\xcf\xb7\xaf\xfd\x0a\xd6\x55\x55\xfc\x25\xe4\x87\x5d\xc2\x63\x08\ +\x78\x2b\x8e\x10\x1a\x6d\x06\x78\x4d\xaa\x02\xd2\xd8\x60\xbd\x61\ +\x0d\xde\x53\x53\x81\x5f\x20\x6d\xeb\x60\x34\xdc\x90\xf5\xb5\xa5\ +\xa3\xd3\xde\xad\xa4\x7d\x3d\x1e\x36\x25\xa9\x1a\x66\x36\x05\xdf\ +\x34\xab\xeb\xdf\x4c\x10\xe4\x05\x77\x4e\x99\xae\xe9\xa6\x8b\xd9\ +\x7f\x3a\x15\x81\x95\x61\x45\x06\x13\x95\xeb\xc0\xa5\xa1\x5b\x0d\ +\x79\x34\x49\x24\x27\x46\xcf\xbd\x8f\x16\xe2\x55\xa2\x16\xb2\x44\ +\xcf\xbb\xee\x41\xbd\x61\x2b\x24\xa1\xd2\x99\xd2\xee\xb9\x31\x09\ +\x68\x08\x38\x39\x14\xdb\xc2\x62\xfb\x83\x56\x5a\x8b\x9a\x4a\xcc\ +\x8d\xda\x28\xb4\x96\xbd\x84\x12\x3f\xc2\x8f\x8c\xd0\x47\x86\xa1\ +\x6f\xcc\xf1\x86\x40\x0f\xad\xea\x80\x89\x38\x97\x28\xbe\x37\x9e\ +\x19\x07\xc3\xc2\x76\x57\x94\xc7\xb3\xed\x96\xe1\x3a\x2e\x0a\x5f\ +\x57\x68\xec\xa2\x21\x51\xd0\x46\xce\xb9\x3a\x88\xb3\x11\x53\x22\ +\x2d\x8f\xf4\xde\xdf\x4f\x21\x1a\xe3\xc6\x8f\x96\xd9\x2a\x89\xee\ +\xcd\xd5\xa5\x44\x49\xe2\xaf\xd2\xd7\x3c\x4a\x66\x46\xd0\x17\x67\ +\xfe\x6a\x15\xa5\xf9\xcc\x68\x0f\x7a\x79\x90\x02\x6b\x7a\x94\x1d\ +\x6b\x6a\xf0\x85\x35\xec\x27\x25\x63\xa5\xc6\xa8\x47\x29\x61\x22\ +\x2e\x6a\x7c\xa5\x72\xbc\xcb\xb6\x9d\x06\x9a\x91\xec\x1a\xd2\xf4\ +\x6a\x89\x2e\x57\x83\x21\x07\x9a\x8c\x18\x00\x52\x95\x0e\x20\x6d\ +\x5a\x33\x34\xba\xc1\xfd\x3a\xa0\x27\xa6\xd8\xb6\xa6\x37\x19\x04\ +\x2e\xc7\x00\x92\x3b\x54\x71\xdc\x5a\xbe\xf1\x5e\x33\x0e\x37\x89\ +\xd7\xd7\x7b\x5a\x1f\x05\xd2\x93\xb5\x97\x7b\xf4\xda\xa3\xfd\x95\ +\x5a\x07\xf3\x7b\xd0\xe1\x84\xee\xd4\x58\x7a\xb3\x82\x44\xac\x5c\ +\x22\x5a\xac\x35\x95\x7c\xaa\xb6\x9f\x42\x43\x30\xcb\x78\xe7\x4c\ +\xc3\xf4\x3e\x2a\x2a\xbf\x99\x79\xf9\x07\x7f\x57\x23\x69\x52\x3f\ +\xcf\xbb\x4e\x17\xb6\x49\xfd\x68\x40\x5c\x53\x02\x34\xdc\xf6\xfd\ +\x38\x58\x94\xbe\x9a\x1c\xee\x60\x64\x14\x1c\x7e\x07\x7f\x05\x44\ +\x8a\x0f\x5a\x3c\x87\xdd\xe3\xd6\x7d\xfb\x17\x7e\x18\x8e\x15\xb0\ +\xe5\x4a\x92\xd5\x6a\x3a\xe9\x40\xc2\x61\x3a\xb9\x80\xf3\x7b\xf8\ +\x12\x7a\xb5\x1f\xc5\x2f\xf0\xa2\xa9\xd1\x78\x30\x1b\xc0\x49\x3e\ +\x19\x57\xeb\x60\xef\x46\x91\xcd\x48\x9f\x75\x93\xd0\xee\x6b\x0c\ +\x32\x53\x70\x27\xe0\xe6\xfc\x51\x7b\x71\x80\x69\x02\x68\xca\x62\ +\x7b\xd4\x7a\x8a\xfd\x10\x8c\x17\x30\x4a\xa9\x74\xa8\x9d\x1b\x85\ +\x4b\xaa\x11\xe4\xa5\x4b\x90\x14\xbe\xd8\x5f\x97\x51\x08\x74\x6e\ +\x36\xbb\x1d\x82\x43\x2c\x2d\xe4\x02\xee\xc9\x09\xeb\xa3\xa4\xa6\ +\x68\xb6\x95\xfe\x73\x29\xcb\xac\x93\xb2\xcc\xee\xa4\x24\x10\xf3\ +\x93\x49\x89\x52\xa8\x88\xa9\x4d\x94\xce\xc5\xcc\x26\xfb\xff\x5c\ +\x4c\x12\x77\x75\x49\xe2\xb9\x94\xe5\x27\x93\x02\x75\x31\x52\x1e\ +\x55\x25\xfd\x64\x52\xa0\xb9\x92\xd8\xd4\x66\x76\xf5\x41\xcc\x67\ +\xbb\xfa\x20\x66\x99\x99\x01\xf0\x40\xcc\xfc\x4f\xd4\x3f\x88\x59\ +\x9b\x3f\xb4\x9b\xa7\xbf\x01\x1e\x88\x0a\xce\ +\x00\x00\x06\x9d\ +\x00\ +\x00\x1d\xe0\x78\x9c\xbd\x59\x5d\xaf\x9b\x38\x10\x7d\xef\xaf\x40\ +\xf4\xa5\xab\x25\xc6\x5f\x80\xc9\x26\xb7\x2f\xd5\x4a\xfb\xb4\xd2\ +\xb6\xd5\x3e\x73\xc1\x37\x61\x9b\x40\x04\xe4\x26\xe9\xaf\xdf\xb1\ +\xc1\x40\x08\x64\x91\xb2\x4a\xaa\x56\xf6\xf8\x80\x8f\x67\xe6\x0c\ +\x63\x75\xf5\xf9\xbc\xdf\x59\xef\xb2\x28\xd3\x3c\x5b\xdb\x04\x61\ +\xdb\x92\x59\x9c\x27\x69\xb6\x59\xdb\xdf\xbf\xfd\xbe\x10\xb6\x55\ +\x56\x51\x96\x44\xbb\x3c\x93\x6b\x3b\xcb\xed\xcf\x2f\x1f\x56\xe5\ +\xfb\xe6\x83\x65\x59\xf0\x70\x56\x2e\x93\x78\x6d\x6f\xab\xea\xb0\ +\x74\xdd\xc3\xb1\xd8\xa1\xbc\xd8\xb8\x49\xec\xca\x9d\xdc\xcb\xac\ +\x2a\x5d\x82\x88\x6b\x77\xf0\xb8\x83\xc7\x85\x8c\xaa\xf4\x5d\xc6\ +\xf9\x7e\x9f\x67\xa5\x7e\x32\x2b\x3f\xf6\xc0\x45\xf2\xd6\xa2\x4f\ +\xa7\x13\x3a\x31\x0d\x22\x61\x18\xba\x98\xba\x94\x2e\x00\xb1\x28\ +\x2f\x59\x15\x9d\x17\xd7\x8f\x02\xc7\xb1\x47\x29\xc6\xd8\x85\xb5\ +\x0e\x39\x0f\xb5\x2c\xc1\x2b\x07\xf8\xdb\xc2\x8d\x01\x95\xf9\xb1\ +\x88\xe5\x1b\x3c\x27\x51\x26\x2b\xf7\xcb\xb7\x2f\xed\xe2\x02\xa3\ +\xa4\x4a\x7a\xaf\x49\xb3\x1f\x65\x1c\x1d\xe4\xd5\xae\xc6\x58\x7b\ +\x20\xda\xcb\xf2\x10\xc5\xb2\x74\x8d\x5d\x3f\x9f\x26\x6b\x1b\x28\ +\x31\x21\xb0\x9e\xf7\xc2\x46\x6a\x40\x83\x5e\xb6\x2b\x18\x71\x81\ +\x18\x22\x56\x11\x0a\xe1\x6b\xd0\x29\x4d\xaa\xed\xda\xf6\xb9\x9e\ +\x6d\x65\xba\xd9\x56\xed\xd4\xb0\x5e\x26\x79\xac\x68\xac\xed\x38\ +\xcf\x20\x4a\x95\x44\xca\x19\x2f\x80\x59\xed\x65\x15\x25\x51\x15\ +\x29\x7c\x4d\xca\x58\x58\x48\xb1\xc6\x00\x0a\xc2\xb2\xfc\xeb\xcb\ +\xef\xf5\x0c\xe6\x71\xbc\xfc\x3b\x2f\x7e\x34\x53\xf8\x29\x40\xf4\ +\x9a\x1f\x61\x73\xfb\xa5\x35\xaf\x92\x78\x09\x8e\xdc\x47\xd5\x4b\ +\xba\x8f\x36\x52\xc5\xe0\x57\x70\xdc\xca\xed\x16\xae\xc0\xd5\xe5\ +\x20\xbb\x97\xd6\xaf\x2d\x64\x1d\x91\xd1\xb4\x4c\xe2\x7d\xaa\x1e\ +\x72\xbf\x56\xe9\x6e\xf7\x87\xda\xc4\xb6\xdc\x96\xa7\xdb\x10\x6d\ +\x8e\xe1\xf6\xce\xb1\x72\xcd\x41\xf5\xac\xf5\x95\x72\x54\xf2\x9e\ +\xca\x53\xfd\x8e\x03\xbc\x31\xce\x77\x79\xb1\xb6\x3f\xbe\xe9\x9f\ +\x5d\x2f\xbc\xe6\x45\x22\x0b\xb3\xe4\xeb\xdf\xd5\x52\x0e\x41\x07\ +\x6e\x10\xd0\xc6\x9c\xbf\xfe\x23\xe3\xaa\xca\x77\xb2\x88\x32\x75\ +\x1e\x82\x9b\x95\x4d\x01\x61\x1c\xb3\x1f\xd3\x44\x8e\x2d\xb4\xb9\ +\xa1\xe8\xb5\x1b\x8d\xae\x96\xdb\x28\xc9\x4f\x6b\x9b\x0e\x17\x4f\ +\x69\x06\x0b\x8b\x26\x83\x88\x8a\xf6\x38\xc2\x64\x15\xc1\x5e\x60\ +\x77\x79\xd2\x3a\x8a\x85\x44\x34\xf6\x72\x9b\x9f\xd4\x61\xd6\x76\ +\x55\x1c\xe5\xf0\x7d\x65\x16\x1d\x16\x59\x9e\x48\x10\xe9\x5b\xb4\ +\x2b\x6f\x00\x3f\xf3\x7c\xbf\xb6\x3d\x84\x03\xce\x7c\xcf\x1b\x2e\ +\xc7\x67\x58\x04\x05\x10\xcc\x04\xbf\x59\x04\x07\xf8\x08\x33\xee\ +\x13\x41\x26\x4e\x72\x1e\xf1\x51\xb3\x34\xe6\xbe\x66\x69\x1f\x9d\ +\xd3\x7d\xfa\x53\x26\x5d\x28\xbb\x5d\x8f\x45\x01\x65\x71\xb1\x8b\ +\x2e\xb2\xe8\x04\xdd\xe4\x5b\x0b\x53\x3e\x31\x79\xad\xd2\x75\x6d\ +\x9f\x2f\xca\x66\x1b\xa3\x72\x99\x32\x80\xe8\x68\x6b\x94\xfb\x83\ +\xaa\x1c\xba\x7a\x7b\xad\xf5\x3d\x2d\xd3\xd7\x9d\xbc\x72\x31\x60\ +\xb3\x08\x8c\xc9\xc0\xaa\x3c\xde\xe0\xd5\xdb\x77\x69\x06\x62\xca\ +\x76\x97\x21\xac\xde\x05\xdc\x23\x0e\xe7\xa1\xf5\x52\x5b\x6b\x55\ +\xad\xdc\x5b\xa1\x68\x7b\x22\xdf\xca\x2e\x37\xd4\x0c\x1c\x41\x8d\ +\x23\x0e\x51\x55\xc9\x22\xeb\x9f\xd6\x94\xa2\x76\xbb\x06\xf3\x3d\ +\x4b\x2b\xc8\x8f\x63\x29\x8b\xaf\xaa\x6c\xfe\x99\x7d\x2f\x3b\x50\ +\x2f\x82\x96\x75\xe9\x4f\x9a\x3c\x46\x9d\xa3\x4c\xde\x82\xa9\x2d\ +\x08\x9b\xae\xba\x94\xd5\x45\x79\xf1\x0d\xea\xc6\x32\x83\xcf\xe1\ +\x6f\x60\x29\xf2\x1f\x72\xf9\x11\xeb\x9f\x99\xd7\x0a\x59\xf6\x5e\ +\x0c\x31\x04\x41\x96\xaa\x80\x41\xc8\xe3\x68\x27\x3f\x21\x4c\x7e\ +\xe9\xad\xeb\x70\x32\x8e\x49\xbf\x18\xc2\x01\xb7\xfd\xe2\xa6\x6a\ +\xad\xe5\x21\xe6\x05\x84\x53\xe1\x30\xe4\x51\x2a\x42\xee\x5b\x91\ +\x45\x10\x83\x4c\x10\x5e\xe0\xb4\x23\x0b\x5b\x04\xfe\x2c\x28\xf2\ +\x03\x98\x12\xcf\xc1\x77\x60\x7d\xd4\x4f\xbb\xbf\xab\xa2\xa6\x98\ +\x30\xf5\x09\xe9\x4a\xe5\x14\x3f\xc2\x11\xc1\x40\x90\x39\x3e\x12\ +\xd4\xe7\xbe\x4f\x80\x1f\x43\x54\x71\xf6\x7c\xa7\x1d\x19\x7e\x3e\ +\x52\xd3\x80\x10\xd8\x79\x1a\xd6\x47\x4d\xf2\x13\xf3\xf8\x09\x5a\ +\xfb\x0f\xa3\x90\x31\x1f\x87\xc0\x0f\x23\xed\x53\x2f\x60\x4e\x37\ +\x34\x0c\x49\xb3\x37\x6c\x7d\x0f\xd7\x83\x4d\x31\x0c\xf1\x0c\x86\ +\x54\xa0\x26\xc2\xc4\x47\x14\x13\x70\x21\x30\xa4\x88\x32\xc5\x3b\ +\x74\xda\x91\xe1\xc7\x11\xf7\x01\xee\xc3\xc6\xd3\xa8\x1e\x68\x92\ +\x1d\x9d\xc1\x0e\x28\x79\x2a\xdb\xa9\x43\x31\xc2\xc2\x63\x21\xd7\ +\xfe\xe3\x61\x13\xf6\x6e\x68\xf8\x81\x9f\x45\x4d\x0a\xdf\x05\xf6\ +\x71\x93\x1c\xf9\x0c\x8e\x8c\xa3\x40\x1d\x9e\x3b\x34\x00\x91\x04\ +\x01\xf5\x34\x47\x5a\xe7\xb8\xde\x9a\x29\xb5\x85\x41\xc7\xd1\xd3\ +\xba\xd2\x14\xa7\x71\x3d\xd8\x24\xc3\x39\x2a\xe1\x18\x11\xb5\x07\ +\x75\x42\xc4\x20\xab\xb1\x20\x9a\x21\x31\xfa\x04\x12\x75\xfc\x44\ +\xc7\xb0\xa6\xa5\x19\x4e\xe3\x7a\xb0\x49\x86\x73\x74\xc2\x9b\x68\ +\x70\x87\x09\x14\xc0\x47\xb2\xd1\x89\xd9\x6e\x94\x21\xd7\x39\x56\ +\xfb\x70\x12\xd7\x83\x4d\x30\x0c\xf1\x1c\x9d\x30\x68\x7c\x7d\xac\ +\x4a\x16\x70\xf5\x02\xbf\xd6\x09\x43\xda\x31\x60\x6d\x47\x86\x5f\ +\x80\xd4\x54\x65\xde\x1d\x54\x0f\x34\xc9\x6e\x8e\x4e\x68\x23\x40\ +\xa6\xfc\x47\x3d\x0f\x33\x4f\xd7\x69\xc5\x58\x6b\xdb\x8c\x0c\xbb\ +\xa6\xe2\xf9\xba\x4c\x4f\xa1\x7a\xa0\x49\x76\x73\x14\xc2\x09\x0a\ +\x55\x0c\xe0\x43\x00\x5b\xf0\x40\x84\xb5\x42\x44\xed\x0c\x95\xf9\ +\x66\xd8\x55\x41\x2d\x8a\x5a\x21\xd3\xb8\x1e\x6c\x92\xe1\x1c\x85\ +\x30\xe8\xcd\x74\x1c\xa8\x87\x02\xe5\xbe\x40\x13\x6c\x3e\x7e\x2a\ +\xf1\xcd\xb0\x4b\xbf\xc0\xf8\xe6\x1e\xae\x07\x9b\x24\x38\x47\x20\ +\xaa\x10\x3a\x9c\x82\x4a\x02\x12\xd4\xda\xf0\xc1\xa2\xff\xed\x7c\ +\xa6\x2c\x23\x2b\xcd\xc2\x14\x01\x32\x27\xff\x49\x80\x1a\x89\x91\ +\x10\x85\x5e\xf3\x9d\x98\xd1\x09\xcc\x6a\x04\xee\xb1\x9b\x93\xff\ +\xa4\xc9\x30\xdf\x21\x02\x74\xea\x05\x1e\xad\xbf\x13\x5a\xfb\xba\ +\x72\x99\x61\x17\x40\xd1\x14\xb5\xbb\xb8\x1e\x6c\x92\xe1\x1c\x0d\ +\xc0\xd7\xd5\xaf\x53\x0c\x34\x1f\x82\x04\x78\x1d\xc5\xba\xc2\x53\ +\xb5\xb3\x19\x76\xf1\xd4\x6a\xac\x3f\x64\xd3\xb8\x1e\x6c\x92\xa1\ +\x7f\x75\xed\xdc\x98\x0b\x67\xd3\xd4\xd6\xad\xb3\xea\x8a\xf5\x68\ +\xd3\x75\xca\x1b\x8e\x43\xd3\xf1\xf7\xba\x4a\xb8\x19\x17\xe9\xf9\ +\x13\xf4\x20\x1e\x87\x0b\x05\x75\x30\xfc\x59\x74\x53\x70\x2e\x5c\ +\x83\x38\x55\x7d\x19\x08\x0a\x8a\x25\x0f\x4c\x03\x7a\xd3\xd8\x0e\ +\xfa\xda\xab\xb6\x16\xb7\xdd\xf1\x95\x57\xcd\xc9\x80\x1d\x6f\x8f\ +\xfc\xfc\x86\xb5\xbb\x67\xe5\x59\x06\x97\xe7\xbc\x58\xc0\x8d\xeb\ +\x3d\xaa\x8e\x85\x54\xcd\xbf\xf1\xf9\x14\x77\xff\x9a\xfb\x53\x9b\ +\xd9\x47\xb9\x8b\x1b\xee\xcf\x6c\x74\x1f\x63\x4f\x30\xbe\x66\x7f\ +\xb7\x09\x66\xa6\xbb\x65\xd3\x2d\xf0\x0d\x66\xb4\x01\x7e\x94\x35\ +\xbd\x66\xfd\x1f\xcd\xb1\xd7\xf5\xbc\xde\xdd\xe6\x78\x04\x38\xde\ +\x1c\x3f\xca\x7f\xa0\xd5\x27\x37\xce\x8f\xb2\x1f\xa8\xf5\xc9\x4d\ +\xf5\xa3\xec\xc5\x0d\xfb\x67\x36\xdc\x0f\xb2\x27\x03\xbd\x3e\xb1\ +\x19\x7f\x94\xf9\x40\xb3\x77\x1b\x75\x66\x3a\x70\x36\xdd\xa6\xdf\ +\x60\x46\x9b\xf4\x47\x59\x0f\x94\xfa\xe4\x06\xfe\x51\xf6\x03\xa5\ +\xde\x6b\xee\x99\x69\xd9\xd9\x74\x63\x7f\x83\x19\x6d\xea\x1f\x25\ +\x2d\x6e\x8b\xfb\xff\xd7\xf0\x3f\x48\x8e\x0e\xf4\xf7\xc4\xcb\xc0\ +\xa3\xcc\x07\xfa\x7b\xf2\x45\xe1\x51\xf6\x03\x1d\xde\xbf\x44\x78\ +\xed\xdd\xc0\xbb\x73\x85\xb8\x45\x8d\x5e\x20\xe6\x31\xd7\x77\x89\ +\x95\xfa\x8f\xb3\x97\x0f\xff\x02\x89\x44\xf7\x33\ \x00\x00\xce\xfd\ \x3c\ \xb8\x64\x18\xca\xef\x9c\x95\xcd\x21\x1c\xbf\x60\xa1\xbd\xdd\x42\ @@ -53402,92 +53633,92 @@ qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x38\x00\x00\x00\x21\ \x00\x00\x00\x38\x00\x02\x00\x00\x00\x05\x00\x00\x00\x1c\ \x00\x00\x00\x1a\x00\x02\x00\x00\x00\x17\x00\x00\x00\x05\ -\x00\x00\x01\xb4\x00\x01\x00\x00\x00\x01\x00\x04\x04\x86\ -\x00\x00\x03\x00\x00\x01\x00\x00\x00\x01\x00\x08\x8b\x49\ -\x00\x00\x02\xde\x00\x01\x00\x00\x00\x01\x00\x08\x4e\x5b\ -\x00\x00\x02\x68\x00\x01\x00\x00\x00\x01\x00\x06\x51\xa0\ -\x00\x00\x03\x3e\x00\x01\x00\x00\x00\x01\x00\x09\x06\x5d\ -\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\xad\x4d\ -\x00\x00\x01\x7c\x00\x01\x00\x00\x00\x01\x00\x02\xef\xc8\ -\x00\x00\x02\x4c\x00\x01\x00\x00\x00\x01\x00\x06\x14\x8d\ -\x00\x00\x02\x8a\x00\x00\x00\x00\x00\x01\x00\x06\x8d\x86\ -\x00\x00\x03\x5a\x00\x00\x00\x00\x00\x01\x00\x09\x43\x86\ -\x00\x00\x01\xd6\x00\x01\x00\x00\x00\x01\x00\x04\x42\x50\ -\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\xde\x04\ -\x00\x00\x01\x60\x00\x01\x00\x00\x00\x01\x00\x02\xb3\x61\ -\x00\x00\x02\x30\x00\x01\x00\x00\x00\x01\x00\x05\xd6\xb0\ -\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x07\x9a\x58\ -\x00\x00\x03\x22\x00\x01\x00\x00\x00\x01\x00\x08\xc9\x30\ -\x00\x00\x02\xa6\x00\x01\x00\x00\x00\x01\x00\x07\x5c\xd3\ -\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x03\ -\x00\x00\x01\x22\x00\x01\x00\x00\x00\x01\x00\x01\xe9\xa3\ -\x00\x00\x01\x98\x00\x00\x00\x00\x00\x01\x00\x03\x2f\x13\ -\x00\x00\x02\x14\x00\x00\x00\x00\x00\x01\x00\x05\x0b\x55\ -\x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x02\x28\xa2\ -\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x04\x80\xc0\ +\x00\x00\x01\xb4\x00\x01\x00\x00\x00\x01\x00\x04\x13\x04\ +\x00\x00\x03\x00\x00\x01\x00\x00\x00\x01\x00\x08\x99\xc7\ +\x00\x00\x02\xde\x00\x01\x00\x00\x00\x01\x00\x08\x5c\xd9\ +\x00\x00\x02\x68\x00\x01\x00\x00\x00\x01\x00\x06\x60\x1e\ +\x00\x00\x03\x3e\x00\x01\x00\x00\x00\x01\x00\x09\x14\xdb\ +\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01\x00\x01\xbb\xcb\ +\x00\x00\x01\x7c\x00\x01\x00\x00\x00\x01\x00\x02\xfe\x46\ +\x00\x00\x02\x4c\x00\x01\x00\x00\x00\x01\x00\x06\x23\x0b\ +\x00\x00\x02\x8a\x00\x00\x00\x00\x00\x01\x00\x06\x9c\x04\ +\x00\x00\x03\x5a\x00\x00\x00\x00\x00\x01\x00\x09\x52\x04\ +\x00\x00\x01\xd6\x00\x01\x00\x00\x00\x01\x00\x04\x50\xce\ +\x00\x00\x00\xea\x00\x00\x00\x00\x00\x01\x00\x00\xec\x82\ +\x00\x00\x01\x60\x00\x01\x00\x00\x00\x01\x00\x02\xc1\xdf\ +\x00\x00\x02\x30\x00\x01\x00\x00\x00\x01\x00\x05\xe5\x2e\ +\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x07\xa8\xd6\ +\x00\x00\x03\x22\x00\x01\x00\x00\x00\x01\x00\x08\xd7\xae\ +\x00\x00\x02\xa6\x00\x01\x00\x00\x00\x01\x00\x07\x6b\x51\ +\x00\x00\x00\xce\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x81\ +\x00\x00\x01\x22\x00\x01\x00\x00\x00\x01\x00\x01\xf8\x21\ +\x00\x00\x01\x98\x00\x00\x00\x00\x00\x01\x00\x03\x3d\x91\ +\x00\x00\x02\x14\x00\x00\x00\x00\x00\x01\x00\x05\x19\xd3\ +\x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x02\x37\x20\ +\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x04\x8f\x3e\ \x00\x00\x00\x4e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x00\x06\x29\ -\x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x01\x64\ -\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc4\ -\x00\x00\x00\x7c\x00\x00\x00\x00\x00\x01\x00\x00\x03\x12\ -\x00\x00\x06\xce\x00\x01\x00\x00\x00\x01\x00\x0a\xf8\xb6\ -\x00\x00\x04\x7c\x00\x00\x00\x00\x00\x01\x00\x0a\x55\x9f\ -\x00\x00\x09\x34\x00\x01\x00\x00\x00\x01\x00\x0b\xcf\xac\ -\x00\x00\x0b\xc4\x00\x01\x00\x00\x00\x01\x00\x0c\xab\xb7\ -\x00\x00\x05\x86\x00\x01\x00\x00\x00\x01\x00\x0a\x95\x4a\ -\x00\x00\x07\x16\x00\x00\x00\x00\x00\x01\x00\x0b\x16\x00\ -\x00\x00\x08\x2a\x00\x01\x00\x00\x00\x01\x00\x0b\x7e\xae\ -\x00\x00\x0b\x26\x00\x01\x00\x00\x00\x01\x00\x0c\x82\x74\ -\x00\x00\x07\x8a\x00\x00\x00\x00\x00\x01\x00\x0b\x3f\xcf\ -\x00\x00\x09\xce\x00\x01\x00\x00\x00\x01\x00\x0c\x10\x4d\ -\x00\x00\x0c\x14\x00\x01\x00\x00\x00\x01\x00\x0c\xc7\xdb\ -\x00\x00\x04\xc2\x00\x01\x00\x00\x00\x01\x00\x0a\x6e\xc1\ -\x00\x00\x08\xdc\x00\x00\x00\x00\x00\x01\x00\x0b\xad\x52\ -\x00\x00\x08\x50\x00\x01\x00\x00\x00\x01\x00\x0b\x84\x72\ -\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x0b\x03\x6a\ -\x00\x00\x04\xe6\x00\x01\x00\x00\x00\x01\x00\x0a\x74\x40\ -\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x0b\x2e\xc5\ -\x00\x00\x04\x9e\x00\x01\x00\x00\x00\x01\x00\x0a\x64\x55\ -\x00\x00\x0b\x4e\x00\x00\x00\x00\x00\x01\x00\x0c\x8d\xe3\ -\x00\x00\x03\xfc\x00\x01\x00\x00\x00\x01\x00\x0a\x30\x0d\ -\x00\x00\x05\xd6\x00\x01\x00\x00\x00\x01\x00\x0a\xb0\x69\ -\x00\x00\x0a\xde\x00\x01\x00\x00\x00\x01\x00\x0c\x6b\x45\ -\x00\x00\x0b\x00\x00\x01\x00\x00\x00\x01\x00\x0c\x78\xe0\ -\x00\x00\x05\xb4\x00\x00\x00\x00\x00\x01\x00\x0a\x9e\x50\ -\x00\x00\x03\xca\x00\x01\x00\x00\x00\x01\x00\x0a\x28\x54\ -\x00\x00\x08\xba\x00\x01\x00\x00\x00\x01\x00\x0b\xa5\xfe\ -\x00\x00\x0a\x28\x00\x00\x00\x00\x00\x01\x00\x0c\x20\x95\ -\x00\x00\x06\x2a\x00\x01\x00\x00\x00\x01\x00\x0a\xc1\xd9\ -\x00\x00\x0a\x4c\x00\x00\x00\x00\x00\x01\x00\x0c\x37\x48\ -\x00\x00\x07\xe4\x00\x00\x00\x00\x00\x01\x00\x0b\x57\x82\ -\x00\x00\x05\x38\x00\x01\x00\x00\x00\x01\x00\x0a\x84\x40\ -\x00\x00\x0b\xe4\x00\x00\x00\x00\x00\x01\x00\x0c\xb6\x65\ -\x00\x00\x06\x84\x00\x00\x00\x00\x00\x01\x00\x0a\xd9\x18\ -\x00\x00\x04\x28\x00\x00\x00\x00\x00\x01\x00\x0a\x38\x14\ -\x00\x00\x0c\x44\x00\x00\x00\x00\x00\x01\x00\x0c\xd3\xb4\ -\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x0c\x54\xcf\ -\x00\x00\x04\x4c\x00\x01\x00\x00\x00\x01\x00\x0a\x4d\x34\ -\x00\x00\x0a\xbc\x00\x01\x00\x00\x00\x01\x00\x0c\x63\xf6\ -\x00\x00\x09\x56\x00\x01\x00\x00\x00\x01\x00\x0b\xd8\x55\ -\x00\x00\x0b\x74\x00\x01\x00\x00\x00\x01\x00\x0c\x96\x70\ -\x00\x00\x06\xac\x00\x01\x00\x00\x00\x01\x00\x0a\xeb\x57\ -\x00\x00\x07\xbc\x00\x01\x00\x00\x00\x01\x00\x0b\x4d\xd1\ -\x00\x00\x09\xac\x00\x00\x00\x00\x00\x01\x00\x0b\xfb\xbc\ -\x00\x00\x05\x5c\x00\x01\x00\x00\x00\x01\x00\x0a\x8b\x09\ -\x00\x00\x08\x0a\x00\x00\x00\x00\x00\x01\x00\x0b\x69\x3a\ -\x00\x00\x06\x0a\x00\x01\x00\x00\x00\x01\x00\x0a\xbc\x5a\ -\x00\x00\x09\xf8\x00\x01\x00\x00\x00\x01\x00\x0c\x16\xc2\ -\x00\x00\x08\x92\x00\x01\x00\x00\x00\x01\x00\x0b\x9a\x8c\ -\x00\x00\x09\x0c\x00\x01\x00\x00\x00\x01\x00\x0b\xbf\xd6\ -\x00\x00\x0b\x9a\x00\x01\x00\x00\x00\x01\x00\x0c\xa1\x35\ -\x00\x00\x0a\x70\x00\x01\x00\x00\x00\x01\x00\x0c\x4a\x1f\ -\x00\x00\x05\x08\x00\x01\x00\x00\x00\x01\x00\x0a\x7c\x1e\ -\x00\x00\x08\x72\x00\x00\x00\x00\x00\x01\x00\x0b\x8a\xba\ -\x00\x00\x06\x58\x00\x00\x00\x00\x00\x01\x00\x0a\xc9\xbe\ -\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x0b\x25\x51\ -\x00\x00\x09\x7a\x00\x00\x00\x00\x00\x01\x00\x0b\xdf\xa6\ -\x00\x00\x03\x76\x00\x01\x00\x00\x00\x01\x00\x0a\x0d\xd7\ -\x00\x00\x03\xa2\x00\x01\x00\x00\x00\x01\x00\x0a\x17\x90\ +\x00\x00\x00\xb0\x00\x01\x00\x00\x00\x01\x00\x00\x16\xe0\ +\x00\x00\x00\x64\x00\x01\x00\x00\x00\x01\x00\x00\x0a\xf1\ +\x00\x00\x00\x96\x00\x01\x00\x00\x00\x01\x00\x00\x13\x00\ +\x00\x00\x00\x7c\x00\x01\x00\x00\x00\x01\x00\x00\x0f\x23\ +\x00\x00\x06\xce\x00\x01\x00\x00\x00\x01\x00\x0b\x07\x34\ +\x00\x00\x04\x7c\x00\x00\x00\x00\x00\x01\x00\x0a\x64\x1d\ +\x00\x00\x09\x34\x00\x01\x00\x00\x00\x01\x00\x0b\xde\x2a\ +\x00\x00\x0b\xc4\x00\x01\x00\x00\x00\x01\x00\x0c\xba\x35\ +\x00\x00\x05\x86\x00\x01\x00\x00\x00\x01\x00\x0a\xa3\xc8\ +\x00\x00\x07\x16\x00\x00\x00\x00\x00\x01\x00\x0b\x24\x7e\ +\x00\x00\x08\x2a\x00\x01\x00\x00\x00\x01\x00\x0b\x8d\x2c\ +\x00\x00\x0b\x26\x00\x01\x00\x00\x00\x01\x00\x0c\x90\xf2\ +\x00\x00\x07\x8a\x00\x00\x00\x00\x00\x01\x00\x0b\x4e\x4d\ +\x00\x00\x09\xce\x00\x01\x00\x00\x00\x01\x00\x0c\x1e\xcb\ +\x00\x00\x0c\x14\x00\x01\x00\x00\x00\x01\x00\x0c\xd6\x59\ +\x00\x00\x04\xc2\x00\x01\x00\x00\x00\x01\x00\x0a\x7d\x3f\ +\x00\x00\x08\xdc\x00\x00\x00\x00\x00\x01\x00\x0b\xbb\xd0\ +\x00\x00\x08\x50\x00\x01\x00\x00\x00\x01\x00\x0b\x92\xf0\ +\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x0b\x11\xe8\ +\x00\x00\x04\xe6\x00\x01\x00\x00\x00\x01\x00\x0a\x82\xbe\ +\x00\x00\x07\x5e\x00\x01\x00\x00\x00\x01\x00\x0b\x3d\x43\ +\x00\x00\x04\x9e\x00\x01\x00\x00\x00\x01\x00\x0a\x72\xd3\ +\x00\x00\x0b\x4e\x00\x00\x00\x00\x00\x01\x00\x0c\x9c\x61\ +\x00\x00\x03\xfc\x00\x01\x00\x00\x00\x01\x00\x0a\x3e\x8b\ +\x00\x00\x05\xd6\x00\x01\x00\x00\x00\x01\x00\x0a\xbe\xe7\ +\x00\x00\x0a\xde\x00\x01\x00\x00\x00\x01\x00\x0c\x79\xc3\ +\x00\x00\x0b\x00\x00\x01\x00\x00\x00\x01\x00\x0c\x87\x5e\ +\x00\x00\x05\xb4\x00\x00\x00\x00\x00\x01\x00\x0a\xac\xce\ +\x00\x00\x03\xca\x00\x01\x00\x00\x00\x01\x00\x0a\x36\xd2\ +\x00\x00\x08\xba\x00\x01\x00\x00\x00\x01\x00\x0b\xb4\x7c\ +\x00\x00\x0a\x28\x00\x00\x00\x00\x00\x01\x00\x0c\x2f\x13\ +\x00\x00\x06\x2a\x00\x01\x00\x00\x00\x01\x00\x0a\xd0\x57\ +\x00\x00\x0a\x4c\x00\x00\x00\x00\x00\x01\x00\x0c\x45\xc6\ +\x00\x00\x07\xe4\x00\x00\x00\x00\x00\x01\x00\x0b\x66\x00\ +\x00\x00\x05\x38\x00\x01\x00\x00\x00\x01\x00\x0a\x92\xbe\ +\x00\x00\x0b\xe4\x00\x00\x00\x00\x00\x01\x00\x0c\xc4\xe3\ +\x00\x00\x06\x84\x00\x00\x00\x00\x00\x01\x00\x0a\xe7\x96\ +\x00\x00\x04\x28\x00\x00\x00\x00\x00\x01\x00\x0a\x46\x92\ +\x00\x00\x0c\x44\x00\x00\x00\x00\x00\x01\x00\x0c\xe2\x32\ +\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x0c\x63\x4d\ +\x00\x00\x04\x4c\x00\x01\x00\x00\x00\x01\x00\x0a\x5b\xb2\ +\x00\x00\x0a\xbc\x00\x01\x00\x00\x00\x01\x00\x0c\x72\x74\ +\x00\x00\x09\x56\x00\x01\x00\x00\x00\x01\x00\x0b\xe6\xd3\ +\x00\x00\x0b\x74\x00\x01\x00\x00\x00\x01\x00\x0c\xa4\xee\ +\x00\x00\x06\xac\x00\x01\x00\x00\x00\x01\x00\x0a\xf9\xd5\ +\x00\x00\x07\xbc\x00\x01\x00\x00\x00\x01\x00\x0b\x5c\x4f\ +\x00\x00\x09\xac\x00\x00\x00\x00\x00\x01\x00\x0c\x0a\x3a\ +\x00\x00\x05\x5c\x00\x01\x00\x00\x00\x01\x00\x0a\x99\x87\ +\x00\x00\x08\x0a\x00\x00\x00\x00\x00\x01\x00\x0b\x77\xb8\ +\x00\x00\x06\x0a\x00\x01\x00\x00\x00\x01\x00\x0a\xca\xd8\ +\x00\x00\x09\xf8\x00\x01\x00\x00\x00\x01\x00\x0c\x25\x40\ +\x00\x00\x08\x92\x00\x01\x00\x00\x00\x01\x00\x0b\xa9\x0a\ +\x00\x00\x09\x0c\x00\x01\x00\x00\x00\x01\x00\x0b\xce\x54\ +\x00\x00\x0b\x9a\x00\x01\x00\x00\x00\x01\x00\x0c\xaf\xb3\ +\x00\x00\x0a\x70\x00\x01\x00\x00\x00\x01\x00\x0c\x58\x9d\ +\x00\x00\x05\x08\x00\x01\x00\x00\x00\x01\x00\x0a\x8a\x9c\ +\x00\x00\x08\x72\x00\x00\x00\x00\x00\x01\x00\x0b\x99\x38\ +\x00\x00\x06\x58\x00\x00\x00\x00\x00\x01\x00\x0a\xd8\x3c\ +\x00\x00\x07\x3e\x00\x01\x00\x00\x00\x01\x00\x0b\x33\xcf\ +\x00\x00\x09\x7a\x00\x00\x00\x00\x00\x01\x00\x0b\xee\x24\ +\x00\x00\x03\x76\x00\x01\x00\x00\x00\x01\x00\x0a\x1c\x55\ +\x00\x00\x03\xa2\x00\x01\x00\x00\x00\x01\x00\x0a\x26\x0e\ " def qInitResources(): diff --git a/src/Mod/Draft/InitGui.py b/src/Mod/Draft/InitGui.py index add28732a..f1ae9612c 100644 --- a/src/Mod/Draft/InitGui.py +++ b/src/Mod/Draft/InitGui.py @@ -62,12 +62,15 @@ class DraftWorkbench (Workbench): ".&&.++***,,)))!!", "#==+)!!!!!!!!!!!", " ##+)!!!!!!!!!!!", - " *,,,,,,,,,,,,"};""" - + " *,,,,,,,,,,,,"};""" + MenuText = "Draft" ToolTip = "The Draft module is used for basic 2D CAD Drafting" def Initialize(self): + def QT_TRANSLATE_NOOP(scope, text): + return text + # run self-tests depsOK = False try: @@ -89,19 +92,18 @@ class DraftWorkbench (Workbench): depsOK = True if not depsOK: return - + + # import Draft tools, icons and macros menu try: - import os,macros,DraftTools,DraftGui,Draft_rc + import os,macros,Draft_rc,DraftTools, DraftGui + from DraftTools import translate FreeCADGui.addLanguagePath(":/translations") FreeCADGui.addIconPath(":/icons") - if not hasattr(FreeCADGui.draftToolBar,"loadedPreferences"): - FreeCADGui.addPreferencePage(":/ui/userprefs-base.ui","Draft") - FreeCADGui.addPreferencePage(":/ui/userprefs-import.ui","Draft") - FreeCADGui.draftToolBar.loadedPreferences = True - self.appendMenu(["&Macro",str(DraftTools.translate("draft","Installed Macros"))],macros.macrosList) - Log ('Loading Draft module...done\n') + self.appendMenu(["&Macro",str(translate("draft","Installed Macros"))],macros.macrosList) except: pass + + # setup menus self.cmdList = ["Draft_Line","Draft_Wire","Draft_Circle","Draft_Arc","Draft_Ellipse", "Draft_Polygon","Draft_Rectangle", "Draft_Text", "Draft_Dimension", "Draft_BSpline","Draft_Point", @@ -115,12 +117,23 @@ class DraftWorkbench (Workbench): "Draft_SelectGroup","Draft_SelectPlane","Draft_ToggleSnap", "Draft_ShowSnapBar","Draft_ToggleGrid"] self.lineList = ["Draft_UndoLine","Draft_FinishLine","Draft_CloseLine"] - self.appendToolbar(str(DraftTools.translate("draft","Draft creation tools")),self.cmdList) - self.appendToolbar(str(DraftTools.translate("draft","Draft modification tools")),self.modList) - self.appendMenu(str(DraftTools.translate("draft","&Draft")),self.cmdList+self.modList) - self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Context tools"))],self.treecmdList) - self.appendMenu([str(DraftTools.translate("draft","&Draft")),str(DraftTools.translate("draft","Wire tools"))],self.lineList) - + self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular', + 'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel', + 'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center', + 'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho'] + self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","Draft creation tools"),self.cmdList) + self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","Draft modification tools"),self.modList) + self.appendMenu(str(translate("draft","&Draft")),self.cmdList+self.modList) + self.appendMenu([str(translate("draft","&Draft")),str(translate("draft","Context tools"))],self.treecmdList) + self.appendMenu([str(translate("draft","&Draft")),str(translate("draft","Wire tools"))],self.lineList) + self.appendMenu([str(translate("draft","&Draft")),str(translate("draft","Snapping"))],self.snapList) + if hasattr(FreeCADGui,"draftToolBar"): + if not hasattr(FreeCADGui.draftToolBar,"loadedPreferences"): + FreeCADGui.addPreferencePage(":/ui/userprefs-base.ui","Draft") + FreeCADGui.addPreferencePage(":/ui/userprefs-import.ui","Draft") + FreeCADGui.draftToolBar.loadedPreferences = True + Log ('Loading Draft module...done\n') + def Activated(self): if hasattr(FreeCADGui,"draftToolBar"): FreeCADGui.draftToolBar.Activated() @@ -131,6 +144,8 @@ class DraftWorkbench (Workbench): def Deactivated(self): if hasattr(FreeCADGui,"draftToolBar"): FreeCADGui.draftToolBar.Deactivated() + if hasattr(FreeCADGui,"Snapper"): + FreeCADGui.Snapper.hide() Msg("Draft workbench deactivated\n") def ContextMenu(self, recipient): @@ -154,6 +169,8 @@ class DraftWorkbench (Workbench): # ability to turn off the Draft workbench (since it is also all included in Arch) if not FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("hideDraftWorkbench"): FreeCADGui.addWorkbench(DraftWorkbench) + +# add Import/Export types App.addImportType("Autodesk DXF (*.dxf)","importDXF") App.addImportType("SVG as geometry (*.svg)","importSVG") App.addImportType("Open CAD Format (*.oca *.gcad)","importOCA") @@ -162,7 +179,7 @@ App.addExportType("Autodesk DXF (*.dxf)","importDXF") App.addExportType("Flattened SVG (*.svg)","importSVG") App.addExportType("Open CAD Format (*.oca)","importOCA") -# DWG support +# add DWG support import importDWG if importDWG.getTeighaConverter(): App.addImportType("Autodesk DWG (*.dwg)","importDWG") diff --git a/src/Mod/Draft/Resources/patterns/concrete.svg b/src/Mod/Draft/Resources/patterns/concrete.svg index 2aa5e9565..b919a036c 100644 --- a/src/Mod/Draft/Resources/patterns/concrete.svg +++ b/src/Mod/Draft/Resources/patterns/concrete.svg @@ -1,5 +1,62 @@ - - + + + + + + image/svg+xml + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/Resources/patterns/cross.svg b/src/Mod/Draft/Resources/patterns/cross.svg index 0d4787b29..e77cf8d73 100644 --- a/src/Mod/Draft/Resources/patterns/cross.svg +++ b/src/Mod/Draft/Resources/patterns/cross.svg @@ -1,5 +1,61 @@ - - + + + + + + image/svg+xml + + + + + + + + - \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/src/Mod/Draft/Resources/patterns/line.svg b/src/Mod/Draft/Resources/patterns/line.svg index 9314cd9e4..ecc47cf3d 100644 --- a/src/Mod/Draft/Resources/patterns/line.svg +++ b/src/Mod/Draft/Resources/patterns/line.svg @@ -1,5 +1,61 @@ - - + + + + + + image/svg+xml + + + + + + + + - \ No newline at end of file + + + + + diff --git a/src/Mod/Draft/Resources/patterns/simple.svg b/src/Mod/Draft/Resources/patterns/simple.svg index ef756426d..07b0da6af 100644 --- a/src/Mod/Draft/Resources/patterns/simple.svg +++ b/src/Mod/Draft/Resources/patterns/simple.svg @@ -1,5 +1,62 @@ - - + + + + + + image/svg+xml + + + + + + + + + - \ No newline at end of file + + + + + + + + diff --git a/src/Mod/Draft/Resources/patterns/square.svg b/src/Mod/Draft/Resources/patterns/square.svg index bc4961c62..8cd2b7ff6 100644 --- a/src/Mod/Draft/Resources/patterns/square.svg +++ b/src/Mod/Draft/Resources/patterns/square.svg @@ -1,5 +1,61 @@ - - + + + + + + image/svg+xml + + + + + + + + - \ No newline at end of file + + + + + + + + + diff --git a/src/Mod/Draft/WorkingPlane.py b/src/Mod/Draft/WorkingPlane.py index ddac62c5a..a0057df5e 100644 --- a/src/Mod/Draft/WorkingPlane.py +++ b/src/Mod/Draft/WorkingPlane.py @@ -298,6 +298,16 @@ class plane: return "z" else: return None + + def isGlobal(self): + "returns True if the plane axes are equal to the global axes" + if self.u != Vector(1,0,0): + return False + if self.v != Vector(0,1,0): + return False + if self.axis != Vector(0,0,1): + return False + return True def getPlacementFromPoints(points): "returns a placement from a list of 3 or 4 vectors" diff --git a/src/Mod/Draft/draftlibs/__init__.py b/src/Mod/Draft/draftlibs/__init__.py deleted file mode 100644 index 0f0a32df1..000000000 --- a/src/Mod/Draft/draftlibs/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# this is the init file of the draftlibs library diff --git a/src/Mod/Draft/draftlibs/dxfColorMap.py b/src/Mod/Draft/draftlibs/dxfColorMap.py deleted file mode 100644 index 66c0bd4e9..000000000 --- a/src/Mod/Draft/draftlibs/dxfColorMap.py +++ /dev/null @@ -1,282 +0,0 @@ -# dictionary mapping AutoCAD color indexes with Blender colors - -# -------------------------------------------------------------------------- -# color_map.py Final by Ed Blake (AKA Kitsu) -# -------------------------------------------------------------------------- -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -------------------------------------------------------------------------- - -color_map = { - 0:[0.0, 0.0, 0.0], - 1:[0.99609375, 0.0, 0.0], - 2:[0.99609375, 0.99609375, 0.0], - 3:[0.0, 0.99609375, 0.0], - 4:[0.0, 0.99609375, 0.99609375], - 5:[0.0, 0.0, 0.99609375], - 6:[0.99609375, 0.0, 0.99609375], - 7:[0.99609375, 0.99609375, 0.99609375], - 8:[0.25390625, 0.25390625, 0.25390625], - 9:[0.5, 0.5, 0.5], - 10:[0.99609375, 0.0, 0.0], - 11:[0.99609375, 0.6640625, 0.6640625], - 12:[0.73828125, 0.0, 0.0], - 13:[0.73828125, 0.4921875, 0.4921875], - 14:[0.50390625, 0.0, 0.0], - 15:[0.50390625, 0.3359375, 0.3359375], - 16:[0.40625, 0.0, 0.0], - 17:[0.40625, 0.26953125, 0.26953125], - 18:[0.30859375, 0.0, 0.0], - 19:[0.30859375, 0.20703125, 0.20703125], - 20:[0.99609375, 0.24609375, 0.0], - 21:[0.99609375, 0.74609375, 0.6640625], - 22:[0.73828125, 0.1796875, 0.0], - 23:[0.73828125, 0.55078125, 0.4921875], - 24:[0.50390625, 0.12109375, 0.0], - 25:[0.50390625, 0.375, 0.3359375], - 26:[0.40625, 0.09765625, 0.0], - 27:[0.40625, 0.3046875, 0.26953125], - 28:[0.30859375, 0.07421875, 0.0], - 29:[0.30859375, 0.23046875, 0.20703125], - 30:[0.99609375, 0.49609375, 0.0], - 31:[0.99609375, 0.828125, 0.6640625], - 32:[0.73828125, 0.3671875, 0.0], - 33:[0.73828125, 0.61328125, 0.4921875], - 34:[0.50390625, 0.25, 0.0], - 35:[0.50390625, 0.41796875, 0.3359375], - 36:[0.40625, 0.203125, 0.0], - 37:[0.40625, 0.3359375, 0.26953125], - 38:[0.30859375, 0.15234375, 0.0], - 39:[0.30859375, 0.2578125, 0.20703125], - 40:[0.99609375, 0.74609375, 0.0], - 41:[0.99609375, 0.9140625, 0.6640625], - 42:[0.73828125, 0.55078125, 0.0], - 43:[0.73828125, 0.67578125, 0.4921875], - 44:[0.50390625, 0.375, 0.0], - 45:[0.50390625, 0.4609375, 0.3359375], - 46:[0.40625, 0.3046875, 0.0], - 47:[0.40625, 0.37109375, 0.26953125], - 48:[0.30859375, 0.23046875, 0.0], - 49:[0.30859375, 0.28515625, 0.20703125], - 50:[0.99609375, 0.99609375, 0.0], - 51:[0.99609375, 0.99609375, 0.6640625], - 52:[0.73828125, 0.73828125, 0.0], - 53:[0.73828125, 0.73828125, 0.4921875], - 54:[0.50390625, 0.50390625, 0.0], - 55:[0.50390625, 0.50390625, 0.3359375], - 56:[0.40625, 0.40625, 0.0], - 57:[0.40625, 0.40625, 0.26953125], - 58:[0.30859375, 0.30859375, 0.0], - 59:[0.30859375, 0.30859375, 0.20703125], - 60:[0.74609375, 0.99609375, 0.0], - 61:[0.9140625, 0.99609375, 0.6640625], - 62:[0.55078125, 0.73828125, 0.0], - 63:[0.67578125, 0.73828125, 0.4921875], - 64:[0.375, 0.50390625, 0.0], - 65:[0.4609375, 0.50390625, 0.3359375], - 66:[0.3046875, 0.40625, 0.0], - 67:[0.37109375, 0.40625, 0.26953125], - 68:[0.23046875, 0.30859375, 0.0], - 69:[0.28515625, 0.30859375, 0.20703125], - 70:[0.49609375, 0.99609375, 0.0], - 71:[0.828125, 0.99609375, 0.6640625], - 72:[0.3671875, 0.73828125, 0.0], - 73:[0.61328125, 0.73828125, 0.4921875], - 74:[0.25, 0.50390625, 0.0], - 75:[0.41796875, 0.50390625, 0.3359375], - 76:[0.203125, 0.40625, 0.0], - 77:[0.3359375, 0.40625, 0.26953125], - 78:[0.15234375, 0.30859375, 0.0], - 79:[0.2578125, 0.30859375, 0.20703125], - 80:[0.24609375, 0.99609375, 0.0], - 81:[0.74609375, 0.99609375, 0.6640625], - 82:[0.1796875, 0.73828125, 0.0], - 83:[0.55078125, 0.73828125, 0.4921875], - 84:[0.12109375, 0.50390625, 0.0], - 85:[0.375, 0.50390625, 0.3359375], - 86:[0.09765625, 0.40625, 0.0], - 87:[0.3046875, 0.40625, 0.26953125], - 88:[0.07421875, 0.30859375, 0.0], - 89:[0.23046875, 0.30859375, 0.20703125], - 90:[0.0, 0.99609375, 0.0], - 91:[0.6640625, 0.99609375, 0.6640625], - 92:[0.0, 0.73828125, 0.0], - 93:[0.4921875, 0.73828125, 0.4921875], - 94:[0.0, 0.50390625, 0.0], - 95:[0.3359375, 0.50390625, 0.3359375], - 96:[0.0, 0.40625, 0.0], - 97:[0.26953125, 0.40625, 0.26953125], - 98:[0.0, 0.30859375, 0.0], - 99:[0.20703125, 0.30859375, 0.20703125], - 100:[0.0, 0.99609375, 0.24609375], - 101:[0.6640625, 0.99609375, 0.74609375], - 102:[0.0, 0.73828125, 0.1796875], - 103:[0.4921875, 0.73828125, 0.55078125], - 104:[0.0, 0.50390625, 0.12109375], - 105:[0.3359375, 0.50390625, 0.375], - 106:[0.0, 0.40625, 0.09765625], - 107:[0.26953125, 0.40625, 0.3046875], - 108:[0.0, 0.30859375, 0.07421875], - 109:[0.20703125, 0.30859375, 0.23046875], - 110:[0.0, 0.99609375, 0.49609375], - 111:[0.6640625, 0.99609375, 0.828125], - 112:[0.0, 0.73828125, 0.3671875], - 113:[0.4921875, 0.73828125, 0.61328125], - 114:[0.0, 0.50390625, 0.25], - 115:[0.3359375, 0.50390625, 0.41796875], - 116:[0.0, 0.40625, 0.203125], - 117:[0.26953125, 0.40625, 0.3359375], - 118:[0.0, 0.30859375, 0.15234375], - 119:[0.20703125, 0.30859375, 0.2578125], - 120:[0.0, 0.99609375, 0.74609375], - 121:[0.6640625, 0.99609375, 0.9140625], - 122:[0.0, 0.73828125, 0.55078125], - 123:[0.4921875, 0.73828125, 0.67578125], - 124:[0.0, 0.50390625, 0.375], - 125:[0.3359375, 0.50390625, 0.4609375], - 126:[0.0, 0.40625, 0.3046875], - 127:[0.26953125, 0.40625, 0.37109375], - 128:[0.0, 0.30859375, 0.23046875], - 129:[0.20703125, 0.30859375, 0.28515625], - 130:[0.0, 0.99609375, 0.99609375], - 131:[0.6640625, 0.99609375, 0.99609375], - 132:[0.0, 0.73828125, 0.73828125], - 133:[0.4921875, 0.73828125, 0.73828125], - 134:[0.0, 0.50390625, 0.50390625], - 135:[0.3359375, 0.50390625, 0.50390625], - 136:[0.0, 0.40625, 0.40625], - 137:[0.26953125, 0.40625, 0.40625], - 138:[0.0, 0.30859375, 0.30859375], - 139:[0.20703125, 0.30859375, 0.30859375], - 140:[0.0, 0.74609375, 0.99609375], - 141:[0.6640625, 0.9140625, 0.99609375], - 142:[0.0, 0.55078125, 0.73828125], - 143:[0.4921875, 0.67578125, 0.73828125], - 144:[0.0, 0.375, 0.50390625], - 145:[0.3359375, 0.4609375, 0.50390625], - 146:[0.0, 0.3046875, 0.40625], - 147:[0.26953125, 0.37109375, 0.40625], - 148:[0.0, 0.23046875, 0.30859375], - 149:[0.20703125, 0.28515625, 0.30859375], - 150:[0.0, 0.49609375, 0.99609375], - 151:[0.6640625, 0.828125, 0.99609375], - 152:[0.0, 0.3671875, 0.73828125], - 153:[0.4921875, 0.61328125, 0.73828125], - 154:[0.0, 0.25, 0.50390625], - 155:[0.3359375, 0.41796875, 0.50390625], - 156:[0.0, 0.203125, 0.40625], - 157:[0.26953125, 0.3359375, 0.40625], - 158:[0.0, 0.15234375, 0.30859375], - 159:[0.20703125, 0.2578125, 0.30859375], - 160:[0.0, 0.24609375, 0.99609375], - 161:[0.6640625, 0.74609375, 0.99609375], - 162:[0.0, 0.1796875, 0.73828125], - 163:[0.4921875, 0.55078125, 0.73828125], - 164:[0.0, 0.12109375, 0.50390625], - 165:[0.3359375, 0.375, 0.50390625], - 166:[0.0, 0.09765625, 0.40625], - 167:[0.26953125, 0.3046875, 0.40625], - 168:[0.0, 0.07421875, 0.30859375], - 169:[0.20703125, 0.23046875, 0.30859375], - 170:[0.0, 0.0, 0.99609375], - 171:[0.6640625, 0.6640625, 0.99609375], - 172:[0.0, 0.0, 0.73828125], - 173:[0.4921875, 0.4921875, 0.73828125], - 174:[0.0, 0.0, 0.50390625], - 175:[0.3359375, 0.3359375, 0.50390625], - 176:[0.0, 0.0, 0.40625], - 177:[0.26953125, 0.26953125, 0.40625], - 178:[0.0, 0.0, 0.30859375], - 179:[0.20703125, 0.20703125, 0.30859375], - 180:[0.24609375, 0.0, 0.99609375], - 181:[0.74609375, 0.6640625, 0.99609375], - 182:[0.1796875, 0.0, 0.73828125], - 183:[0.55078125, 0.4921875, 0.73828125], - 184:[0.12109375, 0.0, 0.50390625], - 185:[0.375, 0.3359375, 0.50390625], - 186:[0.09765625, 0.0, 0.40625], - 187:[0.3046875, 0.26953125, 0.40625], - 188:[0.07421875, 0.0, 0.30859375], - 189:[0.23046875, 0.20703125, 0.30859375], - 190:[0.49609375, 0.0, 0.99609375], - 191:[0.828125, 0.6640625, 0.99609375], - 192:[0.3671875, 0.0, 0.73828125], - 193:[0.61328125, 0.4921875, 0.73828125], - 194:[0.25, 0.0, 0.50390625], - 195:[0.41796875, 0.3359375, 0.50390625], - 196:[0.203125, 0.0, 0.40625], - 197:[0.3359375, 0.26953125, 0.40625], - 198:[0.15234375, 0.0, 0.30859375], - 199:[0.2578125, 0.20703125, 0.30859375], - 200:[0.74609375, 0.0, 0.99609375], - 201:[0.9140625, 0.6640625, 0.99609375], - 202:[0.55078125, 0.0, 0.73828125], - 203:[0.67578125, 0.4921875, 0.73828125], - 204:[0.375, 0.0, 0.50390625], - 205:[0.4609375, 0.3359375, 0.50390625], - 206:[0.3046875, 0.0, 0.40625], - 207:[0.37109375, 0.26953125, 0.40625], - 208:[0.23046875, 0.0, 0.30859375], - 209:[0.28515625, 0.20703125, 0.30859375], - 210:[0.99609375, 0.0, 0.99609375], - 211:[0.99609375, 0.6640625, 0.99609375], - 212:[0.73828125, 0.0, 0.73828125], - 213:[0.73828125, 0.4921875, 0.73828125], - 214:[0.50390625, 0.0, 0.50390625], - 215:[0.50390625, 0.3359375, 0.50390625], - 216:[0.40625, 0.0, 0.40625], - 217:[0.40625, 0.26953125, 0.40625], - 218:[0.30859375, 0.0, 0.30859375], - 219:[0.30859375, 0.20703125, 0.30859375], - 220:[0.99609375, 0.0, 0.74609375], - 221:[0.99609375, 0.6640625, 0.9140625], - 222:[0.73828125, 0.0, 0.55078125], - 223:[0.73828125, 0.4921875, 0.67578125], - 224:[0.50390625, 0.0, 0.375], - 225:[0.50390625, 0.3359375, 0.4609375], - 226:[0.40625, 0.0, 0.3046875], - 227:[0.40625, 0.26953125, 0.37109375], - 228:[0.30859375, 0.0, 0.23046875], - 229:[0.30859375, 0.20703125, 0.28515625], - 230:[0.99609375, 0.0, 0.49609375], - 231:[0.99609375, 0.6640625, 0.828125], - 232:[0.73828125, 0.0, 0.3671875], - 233:[0.73828125, 0.4921875, 0.61328125], - 234:[0.50390625, 0.0, 0.25], - 235:[0.50390625, 0.3359375, 0.41796875], - 236:[0.40625, 0.0, 0.203125], - 237:[0.40625, 0.26953125, 0.3359375], - 238:[0.30859375, 0.0, 0.15234375], - 239:[0.30859375, 0.20703125, 0.2578125], - 240:[0.99609375, 0.0, 0.24609375], - 241:[0.99609375, 0.6640625, 0.74609375], - 242:[0.73828125, 0.0, 0.1796875], - 243:[0.73828125, 0.4921875, 0.55078125], - 244:[0.50390625, 0.0, 0.12109375], - 245:[0.50390625, 0.3359375, 0.375], - 246:[0.40625, 0.0, 0.09765625], - 247:[0.40625, 0.26953125, 0.3046875], - 248:[0.30859375, 0.0, 0.07421875], - 249:[0.30859375, 0.20703125, 0.23046875], - 250:[0.19921875, 0.19921875, 0.19921875], - 251:[0.3125, 0.3125, 0.3125], - 252:[0.41015625, 0.41015625, 0.41015625], - 253:[0.5078125, 0.5078125, 0.5078125], - 254:[0.7421875, 0.7421875, 0.7421875], - 255:[0.99609375, 0.99609375, 0.99609375], -} diff --git a/src/Mod/Draft/draftlibs/dxfImportObjects.py b/src/Mod/Draft/draftlibs/dxfImportObjects.py deleted file mode 100644 index 27406b819..000000000 --- a/src/Mod/Draft/draftlibs/dxfImportObjects.py +++ /dev/null @@ -1,1330 +0,0 @@ -"""This module provides wrapper objects for dxf entities. - - The wrappers expect a "dxf object" as input. The dxf object is - an object with a type and a data attribute. Type is a lowercase - string matching the 0 code of a dxf entity. Data is a list containing - dxf objects or lists of [code, data] pairs. - - This module is not general, and is only for dxf import. -""" - -# -------------------------------------------------------------------------- -# DXF Import Objects v0.8 by Ed Blake (AKA Kitsu) -# -------------------------------------------------------------------------- -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -------------------------------------------------------------------------- -from math import * - - -# from Stani's dxf writer v1.1 (c)www.stani.be (GPL) -#---color values -BYBLOCK=0 -BYLAYER=256 - -#---block-type flags (bit coded values, may be combined): -ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application -NON_CONSTANT_ATTRIBUTES =2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all) -XREF =4 # This block is an external reference (xref) -XREF_OVERLAY =8 # This block is an xref overlay -EXTERNAL =16 # This block is externally dependent -RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input) -REFERENCED =64 # This definition is a referenced external reference (ignored on input) - -#---mtext flags -#attachment point -TOP_LEFT = 1 -TOP_CENTER = 2 -TOP_RIGHT = 3 -MIDDLE_LEFT = 4 -MIDDLE_CENTER = 5 -MIDDLE_RIGHT = 6 -BOTTOM_LEFT = 7 -BOTTOM_CENTER = 8 -BOTTOM_RIGHT = 9 -#drawing direction -LEFT_RIGHT = 1 -TOP_BOTTOM = 3 -BY_STYLE = 5 #the flow direction is inherited from the associated text style -#line spacing style (optional): -AT_LEAST = 1 #taller characters will override -EXACT = 2 #taller characters will not override - -#---polyline flags -CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction) -CURVE_FIT =2 # Curve-fit vertices have been added -SPLINE_FIT =4 # Spline-fit vertices have been added -POLYLINE_3D =8 # This is a 3D polyline -POLYGON_MESH =16 # This is a 3D polygon mesh -CLOSED_N =32 # The polygon mesh is closed in the N direction -POLYFACE_MESH =64 # The polyline is a polyface mesh -CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline - -#---text flags -#horizontal -LEFT = 0 -CENTER = 1 -RIGHT = 2 -ALIGNED = 3 #if vertical alignment = 0 -MIDDLE = 4 #if vertical alignment = 0 -FIT = 5 #if vertical alignment = 0 -#vertical -BASELINE = 0 -BOTTOM = 1 -MIDDLE = 2 -TOP = 3 -class Object: - """Empty container class for dxf objects""" - - def __init__(self, _type=''): - """_type expects a string value.""" - self.type = _type - self.name = '' - self.data = [] - - def __str__(self): - if self.name: - return self.name - else: - return self.type - - def __repr__(self): - return str(self.data) - - def get_type(self, kind=''): - """Despite the name, this method actually returns all objects of type 'kind' from self.data.""" - if type: - objects = [] - for item in self.data: - if type(item) != list and item.type == kind: - # we want this type of object - objects.append(item) - elif type(item) == list and item[0] == kind: - # we want this type of data - objects.append(item[1]) - return objects - - -class Layer: - """Class for objects representing dxf layers.""" - - def __init__(self, obj): - """Expects an entity object of type line as input.""" - self.type = obj.type - self.data = obj.data[:] - - self.name = obj.get_type(2)[0] - self.color = obj.get_type(62)[0] - self.flags = obj.get_type(70)[0] - self.frozen = self.flags&1 - - - - def __repr__(self): - return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color) - - - -class Line: - """Class for objects representing dxf lines.""" - - def __init__(self, obj): - """Expects an entity object of type line as input.""" - if not obj.type == 'line': - raise TypeError, "Wrong type %s for line object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - - self.points = self.get_points(obj.data) - - - - - def get_points(self, data): - """Gets start and end points for a line type object. - - Lines have a fixed number of points (two) and fixed codes for each value. - """ - - # start x, y, z and end x, y, z = 0 - sx, sy, sz, ex, ey, ez = 0, 0, 0, 0, 0, 0 - for item in data: - if item[0] == 10: # 10 = x - sx = item[1] - elif item[0] == 20: # 20 = y - sy = item[1] - elif item[0] == 30: # 30 = z - sz = item[1] - elif item[0] == 11: # 11 = x - ex = item[1] - elif item[0] == 21: # 21 = y - ey = item[1] - elif item[0] == 31: # 31 = z - ez = item[1] - return [[sx, sy, sz], [ex, ey, ez]] - - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - -class LWpolyline: - """Class for objects representing dxf LWpolylines.""" - - def __init__(self, obj): - """Expects an entity object of type lwpolyline as input.""" - if not obj.type == 'lwpolyline': - raise TypeError, "Wrong type %s for polyline object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.num_points = obj.get_type(90)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.elevation = obj.get_type(38) - if self.elevation: - self.elevation = self.elevation[0] - else: - self.elevation = 0 - - self.flags = obj.get_type(70) - if self.flags: - self.flags = self.flags[0] - else: - self.flags = 0 - - self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.points = self.get_points(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - - def get_points(self, data): - """Gets points for a polyline type object. - - Polylines have no fixed number of verts, and - each vert can have a number of properties. - Verts should be coded as - 10:xvalue - 20:yvalue - 40:startwidth or 0 - 41:endwidth or 0 - 42:bulge or 0 - for each vert - """ - num = self.num_points - point = None - points = [] - for item in data: - if item[0] == 10: # 10 = x - if point: - points.append(point) - point = Vertex() - point.x = item[1] - elif item[0] == 20: # 20 = y - point.y = item[1] - elif item[0] == 40: # 40 = start width - point.swidth = item[1] - elif item[0] == 41: # 41 = end width - point.ewidth = item[1] - elif item[0] == 42: # 42 = bulge - point.bulge = item[1] - points.append(point) - return points - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - -class Polyline: - """Class for objects representing dxf LWpolylines.""" - - def __init__(self, obj): - """Expects an entity object of type polyline as input.""" - if not obj.type == 'polyline': - raise TypeError, "Wrong type %s for polyline object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - self.points = [] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.elevation = obj.get_type(30) - if self.elevation: - self.elevation = self.elevation[0] - else: - self.elevation = 0 - - self.flags = obj.get_type(70) - if self.flags: - self.flags = self.flags[0] - else: - self.flags = 0 - - self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - -class Vertex(object): - """Generic vertex object used by polylines (and maybe others).""" - - def __init__(self, obj=None): - """Initializes vertex data. - - The optional obj arg is an entity object of type vertex. - """ - self.loc = [0,0,0] - self.bulge = 0 - self.swidth = 0 - self.ewidth = 0 - self.flags = 0 - - if obj is not None: - if not obj.type == 'vertex': - raise TypeError, "Wrong type %s for vertex object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - self.get_props(obj.data) - - - def get_props(self, data): - """Gets coords for a vertex type object. - - Each vert can have a number of properties. - Verts should be coded as - 10:xvalue - 20:yvalue - 40:startwidth or 0 - 41:endwidth or 0 - 42:bulge or 0 - """ - for item in data: - if item[0] == 10: # 10 = x - self.x = item[1] - elif item[0] == 20: # 20 = y - self.y = item[1] - elif item[0] == 30: # 30 = z - self.z = item[1] - elif item[0] == 40: # 40 = start width - self.swidth = item[1] - elif item[0] == 41: # 41 = end width - self.ewidth = item[1] - elif item[0] == 42: # 42 = bulge - self.bulge = item[1] - elif item[0] == 70: # 70 = vert flags - self.flags = item[1] - - - def __len__(self): - return 3 - - - def __getitem__(self, key): - return self.loc[key] - - - def __setitem__(self, key, value): - if key in [0,1,2]: - self.loc[key] - - - def __iter__(self): - return self.loc.__iter__() - - - def __str__(self): - return str(self.loc) - - - def __repr__(self): - return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s" %(self.loc, self.swidth, self.ewidth, self.bulge) - - - def getx(self): - return self.loc[0] - - def setx(self, value): - self.loc[0] = value - - x = property(getx, setx) - - - def gety(self): - return self.loc[1] - - def sety(self, value): - self.loc[1] = value - - y = property(gety, sety) - - - def getz(self): - return self.loc[2] - - def setz(self, value): - self.loc[2] = value - - z = property(getz, setz) - - - -class Text: - """Class for objects representing dxf Text.""" - - def __init__(self, obj): - """Expects an entity object of type text as input.""" - if not obj.type == 'text': - raise TypeError, "Wrong type %s for text object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.height = obj.get_type(40)[0] - self.value = obj.get_type(1)[0] # The text string value - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.rotation = obj.get_type(50) # radians? - if not self.rotation: - self.rotation = 0 - else: - self.rotation = self.rotation[0] - - self.width_factor = obj.get_type(41) # Scaling factor along local x axis - if not self.width_factor: - self.width_factor = 1 - else: - self.width_factor = self.width_factor[0] - - self.oblique = obj.get_type(51) # skew in degrees -90 <= oblique <= 90 - if not self.oblique: - self.oblique = 0 - else: - self.oblique = self.oblique[0] - - self.halignment = obj.get_type(72) # horiz. alignment - if not self.halignment: # 0=left, 1=center, 2=right, 3=aligned, 4=middle, 5=fit - self.halignment = 0 - else: - self.halignment = self.halignment[0] - - self.valignment = obj.get_type(73) # vert. alignment - if not self.valignment: # 0=baseline, 1=bottom, 2=middle, 3=top - self.valignment = 0 - else: - self.valignment = self.valignment[0] - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data, self.halignment, self.valignment) - self.extrusion = self.get_extrusion(obj.data) - - - - - def get_loc(self, data, halign, valign): - """Gets adjusted location for text type objects. - - If group 72 and/or 73 values are nonzero then the first alignment point values - are ignored and AutoCAD calculates new values based on the second alignment - point and the length and height of the text string itself (after applying the - text style). If the 72 and 73 values are zero or missing, then the second - alignment point is meaningless. - - I don't know how to calc text size... - """ - # bottom left x, y, z and justification x, y, z = 0 - x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0 - for item in data: - if item[0] == 10: # 10 = x - x = item[1] - elif item[0] == 20: # 20 = y - y = item[1] - elif item[0] == 30: # 30 = z - z = item[1] - elif item[0] == 11: # 11 = x - jx = item[1] - elif item[0] == 21: # 21 = y - jy = item[1] - elif item[0] == 31: # 31 = z - jz = item[1] - - if halign or valign: - x, y, z = jx, jy, jz - return [x, y, z] - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) - - - -class Mtext: - """Class for objects representing dxf Mtext.""" - - def __init__(self, obj): - """Expects an entity object of type mtext as input.""" - if not obj.type == 'mtext': - raise TypeError, "Wrong type %s for mtext object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.height = obj.get_type(40)[0] - self.width = obj.get_type(41)[0] - self.alignment = obj.get_type(71)[0] # alignment 1=TL, 2=TC, 3=TR, 4=ML, 5=MC, 6=MR, 7=BL, 8=BC, 9=BR - self.value = self.get_text(obj.data) # The text string value - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.rotation = obj.get_type(50) # radians - if not self.rotation: - self.rotation = 0 - else: - self.rotation = self.rotation[0] - - self.width_factor = obj.get_type(42) # Scaling factor along local x axis - if not self.width_factor: - self.width_factor = 1 - else: - self.width_factor = self.width_factor[0] - - self.line_space = obj.get_type(44) # percentage of default - if not self.line_space: - self.line_space = 1 - else: - self.line_space = self.line_space[0] - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_text(self, data): - """Reconstructs mtext data from dxf codes.""" - primary = '' - secondary = [] - for item in data: - if item[0] == 1: # There should be only one primary... - primary = item[1] - elif item[0] == 3: # There may be any number of extra strings (in order) - secondary.append(item[1]) - if not primary: - #raise ValueError, "Empty Mtext Object!" - string = "Empty Mtext Object!" - if not secondary: - string = primary.replace(r'\P', '\n') - else: - string = ''.join(secondary)+primary - string = string.replace(r'\P', '\n') - return string - def get_loc(self, data): - """Gets location for a mtext type objects. - - Mtext objects have only one point indicating location. - """ - loc = [0,0,0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) - - - -class Circle: - """Class for objects representing dxf Circles.""" - - def __init__(self, obj): - """Expects an entity object of type circle as input.""" - if not obj.type == 'circle': - raise TypeError, "Wrong type %s for circle object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.radius = obj.get_type(40)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for circle type objects. - - Circles have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - -class Arc: - """Class for objects representing dxf arcs.""" - - def __init__(self, obj): - """Expects an entity object of type arc as input.""" - if not obj.type == 'arc': - raise TypeError, "Wrong type %s for arc object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.radius = obj.get_type(40)[0] - self.start_angle = obj.get_type(50)[0] - self.end_angle = obj.get_type(51)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for arc type objects. - - Arcs have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - -class BlockRecord: - """Class for objects representing dxf block_records.""" - - def __init__(self, obj): - """Expects an entity object of type block_record as input.""" - if not obj.type == 'block_record': - raise TypeError, "Wrong type %s for block_record object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.name = obj.get_type(2)[0] - - # optional data (with defaults) - self.insertion_units = obj.get_type(70) - if not self.insertion_units: - self.insertion_units = None - else: - self.insertion_units = self.insertion_units[0] - - self.insert_units = obj.get_type(1070) - if not self.insert_units: - self.insert_units = None - else: - self.insert_units = self.insert_units[0] - - - - - - - def __repr__(self): - return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units) - - - - -class Block: - """Class for objects representing dxf blocks.""" - - def __init__(self, obj): - """Expects an entity object of type block as input.""" - if not obj.type == 'block': - raise TypeError, "Wrong type %s for block object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.flags = obj.get_type(70)[0] - self.entities = Object('block_contents') - self.entities.data = objectify([ent for ent in obj.data if type(ent) != list]) - - # optional data (with defaults) - self.name = obj.get_type(3) - if self.name: - self.name = self.name[0] - else: - self.name = obj.get_type(2) - if self.name: - self.name = self.name[0] - else: - self.name = 'blank' - - self.path = obj.get_type(1) - if self.path: - self.path = self.path[0] - else: - self.path = '' - - self.discription = obj.get_type(4) - if self.discription: - self.discription = self.discription[0] - else: - self.discription = '' - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - - - - - - def get_loc(self, data): - """Gets the insert point of the block.""" - loc = [0, 0, 0] - for item in data: - if type(item) != list: - continue - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def __repr__(self): - return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path) - - - - -class Insert: - """Class for objects representing dxf inserts.""" - - def __init__(self, obj): - """Expects an entity object of type insert as input.""" - if not obj.type == 'insert': - raise TypeError, "Wrong type %s for insert object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.block = obj.get_type(2)[0] - - # optional data (with defaults) - self.rotation = obj.get_type(50) - if self.rotation: - self.rotation = self.rotation[0] - else: - self.rotation = 0 - - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - self.scale = self.get_scale(obj.data) - self.rows, self.columns = self.get_array(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for circle type objects. - - Circles have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_scale(self, data): - """Gets the x/y/z scale factor for the block. - """ - scale = [1, 1, 1] - for item in data: - if item[0] == 41: # 41 = x scale - scale[0] = item[1] - elif item[0] == 42: # 42 = y scale - scale[1] = item[1] - elif item[0] == 43: # 43 = z scale - scale[2] = item[1] - return scale - - - - def get_array(self, data): - """Returns the pair (row number, row spacing), (column number, column spacing).""" - columns = 1 - rows = 1 - cspace = 0 - rspace = 0 - for item in data: - if item[0] == 70: # 70 = columns - columns = item[1] - elif item[0] == 71: # 71 = rows - rows = item[1] - if item[0] == 44: # 44 = columns - cspace = item[1] - elif item[0] == 45: # 45 = rows - rspace = item[1] - return (rows, rspace), (columns, cspace) - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, block - %s" %(self.__class__.__name__, self.layer, self.block) - - - - -class Ellipse: - """Class for objects representing dxf ellipses.""" - - def __init__(self, obj): - """Expects an entity object of type ellipse as input.""" - if not obj.type == 'ellipse': - raise TypeError, "Wrong type %s for ellipse object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.ratio = obj.get_type(40)[0] - self.start_angle = obj.get_type(41)[0] - self.end_angle = obj.get_type(42)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.loc = self.get_loc(obj.data) - self.major = self.get_major(obj.data) - self.extrusion = self.get_extrusion(obj.data) - self.radius = sqrt(self.major[0]**2 + self.major[0]**2 + self.major[0]**2) - - - - - def get_loc(self, data): - """Gets the center location for arc type objects. - - Arcs have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_major(self, data): - """Gets the major axis for ellipse type objects. - - The ellipse major axis defines the rotation of the ellipse and its radius. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 11: # 11 = x - loc[0] = item[1] - elif item[0] == 21: # 21 = y - loc[1] = item[1] - elif item[0] == 31: # 31 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - -class Face: - """Class for objects representing dxf 3d faces.""" - - def __init__(self, obj): - """Expects an entity object of type 3dfaceplot as input.""" - if not obj.type == '3dface': - raise TypeError, "Wrong type %s for 3dface object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer, discard_index = get_layer(obj.data) - del obj.data[discard_index] - self.points = self.get_points(obj.data) - - - - - def get_points(self, data): - """Gets 3-4 points for a 3d face type object. - - Faces have three or optionally four verts. - """ - - a = [0, 0, 0] - b = [0, 0, 0] - c = [0, 0, 0] - d = False - for item in data: - # ----------- a ------------- - if item[0] == 10: # 10 = x - a[0] = item[1] - elif item[0] == 20: # 20 = y - a[1] = item[1] - elif item[0] == 30: # 30 = z - a[2] = item[1] - # ----------- b ------------- - elif item[0] == 11: # 11 = x - b[0] = item[1] - elif item[0] == 21: # 21 = y - b[1] = item[1] - elif item[0] == 31: # 31 = z - b[2] = item[1] - # ----------- c ------------- - elif item[0] == 12: # 12 = x - c[0] = item[1] - elif item[0] == 22: # 22 = y - c[1] = item[1] - elif item[0] == 32: # 32 = z - c[2] = item[1] - # ----------- d ------------- - elif item[0] == 13: # 13 = x - d = [0, 0, 0] - d[0] = item[1] - elif item[0] == 23: # 23 = y - d[1] = item[1] - elif item[0] == 33: # 33 = z - d[2] = item[1] - out = [a,b,c] - if d: - out.append(d) - return out - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - -def get_name(data): - """Get the name of an object from its object data. - - Returns a pair of (data_item, name) where data_item is the list entry where the name was found - (the data_item can be used to remove the entry from the object data). Be sure to check - name not None before using the returned values! - """ - value = None - for i, item in enumerate(data): - if item[0] == 2: - value = item[1] - break - return item, value, i - -def get_layer(data): - """Expects object data as input. - - Returns (entry, layer_name, entry_index) where entry is the data item that provided the layer name. - """ - value = None - for i, item in enumerate(data): - if item[0] == 8: - value = item[1] - break - return item, value, i - - -# type to object map -type_map = { - 'line':Line, - 'lwpolyline':LWpolyline, - 'text':Text, - 'mtext':Mtext, - 'circle':Circle, - 'arc':Arc, - 'layer':Layer, - 'block_record':BlockRecord, - 'block':Block, - 'insert':Insert, - 'ellipse':Ellipse, - '3dface':Face -} - -def objectify(data): - """Expects a section type object's data as input. - - Maps object data to the correct object type. - """ - objects = [] # colector for finished objects - known_types = type_map.keys() # so we don't have to call foo.keys() every iteration - index = 0 - while index < len(data): - item = data[index] - if type(item) != list and item.type in known_types: - # proccess the object and append the resulting object - objects.append(type_map[item.type](item)) - elif type(item) != list and item.type == 'table': - item.data = objectify(item.data) # tables have sub-objects - objects.append(item) - elif type(item) != list and item.type == 'polyline': - pline = Polyline(item) - while 1: - index += 1 - item = data[index] - if item.type == 'vertex': - v = Vertex(item) - pline.points.append(v) - elif item.type == 'seqend': - break - else: - print "Error: non-vertex found before seqend!" - index -= 1 - break - objects.append(pline) - else: - # we will just let the data pass un-harrased - objects.append(item) - index += 1 - return objects -if __name__ == "__main__": - print "No example yet!" diff --git a/src/Mod/Draft/draftlibs/dxfLibrary.py b/src/Mod/Draft/draftlibs/dxfLibrary.py deleted file mode 100644 index 05a6e4f03..000000000 --- a/src/Mod/Draft/draftlibs/dxfLibrary.py +++ /dev/null @@ -1,896 +0,0 @@ -#dxfLibrary.py : provides functions for generating DXF files -# -------------------------------------------------------------------------- -__version__ = "v1.33 - 2009.06.16" -__author__ = "Stani Michiels(Stani), Remigiusz Fiedler(migius)" -__license__ = "GPL" -__url__ = "http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_dxf" -__bpydoc__ ="""The library to export geometry data to DXF format r12 version. - -Copyright %s -Version %s -License %s -Homepage %s - -See the homepage for documentation. -Dedicated thread on BlenderArtists: http://blenderartists.org/forum/showthread.php?t=136439 - -IDEAs: -- - -TODO: -- add support for DXFr14 (needs extended file header) -- add support for SPLINEs (possible first in DXFr14 version) -- add user preset for floating point precision (3-16?) - -History -v1.33 - 2009.06.16 by migius - - modif _point(): converts all coords to floats - - modif LineType class: implement elements - - added VPORT class, incl. defaults - - fix Insert class -v1.32 - 2009.06.06 by migius - - modif Style class: changed defaults to widthFactor=1.0, obliqueAngle=0.0 - - modif Text class: alignment parameter reactivated -v1.31 - 2009.06.02 by migius - - modif _Entity class: added paperspace,elevation -v1.30 - 2009.05.28 by migius - - bugfix 3dPOLYLINE/POLYFACE: VERTEX needs x,y,z coordinates, index starts with 1 not 0 -v1.29 - 2008.12.28 by Yorik - - modif POLYLINE to support bulge segments -v1.28 - 2008.12.13 by Steeve/BlenderArtists - - bugfix for EXTMIN/EXTMAX to suit Cycas-CAD -v1.27 - 2008.10.07 by migius - - beautifying output code: keys whitespace prefix - - refactoring DXF-strings format: NewLine moved to the end of -v1.26 - 2008.10.05 by migius - - modif POLYLINE to support POLYFACE -v1.25 - 2008.09.28 by migius - - modif FACE class for r12 -v1.24 - 2008.09.27 by migius - - modif POLYLINE class for r12 - - changing output format from r9 to r12(AC1009) -v1.1 (20/6/2005) by www.stani.be/python/sdxf - - Python library to generate dxf drawings -______________________________________________________________ -""" % (__author__,__version__,__license__,__url__) - -# -------------------------------------------------------------------------- -# DXF Library: copyright (C) 2005 by Stani Michiels (AKA Stani) -# 2008/2009 modif by Remigiusz Fiedler (AKA migius) -# -------------------------------------------------------------------------- -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** - - -#import Blender -#from Blender import Mathutils, Window, Scene, sys, Draw -#import BPyMessages - -try: - import copy - #from struct import pack -except: - copy = None - -####1) Private (only for developpers) -_HEADER_POINTS=['insbase','extmin','extmax'] - -#---helper functions----------------------------------- -def _point(x,index=0): - """Convert tuple to a dxf point""" - #print 'deb: _point=', x #------------- - return '\n'.join([' %s\n%s'%((i+1)*10+index,float(x[i])) for i in range(len(x))]) - -def _points(plist): - """Convert a list of tuples to dxf points""" - out = '\n'.join([_point(plist[i],i)for i in range(len(plist))]) - return out - -#---base classes---------------------------------------- -class _Call: - """Makes a callable class.""" - def copy(self): - """Returns a copy.""" - return copy.deepcopy(self) - - def __call__(self,**attrs): - """Returns a copy with modified attributes.""" - copied=self.copy() - for attr in attrs:setattr(copied,attr,attrs[attr]) - return copied - -#------------------------------------------------------- -class _Entity(_Call): - """Base class for _common group codes for entities.""" - def __init__(self,paperspace=None,color=None,layer='0', - lineType=None,lineTypeScale=None,lineWeight=None, - extrusion=None,elevation=None,thickness=None, - parent=None): - """None values will be omitted.""" - self.paperspace = paperspace - self.color = color - self.layer = layer - self.lineType = lineType - self.lineTypeScale = lineTypeScale - self.lineWeight = lineWeight - self.extrusion = extrusion - self.elevation = elevation - self.thickness = thickness - #self.visible = visible - self.parent = parent - - def _common(self): - """Return common group codes as a string.""" - if self.parent:parent=self.parent - else:parent=self - result ='' - if parent.paperspace==1: result+=' 67\n1\n' - if parent.layer!=None: result+=' 8\n%s\n'%parent.layer - if parent.color!=None: result+=' 62\n%s\n'%parent.color - if parent.lineType!=None: result+=' 6\n%s\n'%parent.lineType - # TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight - # TODO: if parent.visible!=None: result+='60\n%s\n'%parent.visible - if parent.lineTypeScale!=None: result+=' 48\n%s\n'%parent.lineTypeScale - if parent.elevation!=None: result+=' 38\n%s\n'%parent.elevation - if parent.thickness!=None: result+=' 39\n%s\n'%parent.thickness - if parent.extrusion!=None: result+='%s\n'%_point(parent.extrusion,200) - return result - -#-------------------------- -class _Entities: - """Base class to deal with composed objects.""" - def __dxf__(self): - return [] - - def __str__(self): - return ''.join([str(x) for x in self.__dxf__()]) - -#-------------------------- -class _Collection(_Call): - """Base class to expose entities methods to main object.""" - def __init__(self,entities=[]): - self.entities=copy.copy(entities) - #link entities methods to drawing - for attr in dir(self.entities): - if attr[0]!='_': - attrObject=getattr(self.entities,attr) - if callable(attrObject): - setattr(self,attr,attrObject) - -####2) Constants -#---color values -BYBLOCK=0 -BYLAYER=256 - -#---block-type flags (bit coded values, may be combined): -ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application -NON_CONSTANT_ATTRIBUTES =2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all) -XREF =4 # This block is an external reference (xref) -XREF_OVERLAY =8 # This block is an xref overlay -EXTERNAL =16 # This block is externally dependent -RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input) -REFERENCED =64 # This definition is a referenced external reference (ignored on input) - -#---mtext flags -#attachment point -TOP_LEFT = 1 -TOP_CENTER = 2 -TOP_RIGHT = 3 -MIDDLE_LEFT = 4 -MIDDLE_CENTER = 5 -MIDDLE_RIGHT = 6 -BOTTOM_LEFT = 7 -BOTTOM_CENTER = 8 -BOTTOM_RIGHT = 9 -#drawing direction -LEFT_RIGHT = 1 -TOP_BOTTOM = 3 -BY_STYLE = 5 #the flow direction is inherited from the associated text style -#line spacing style (optional): -AT_LEAST = 1 #taller characters will override -EXACT = 2 #taller characters will not override - -#---polyline flags -CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction) -CURVE_FIT =2 # Curve-fit vertices have been added -SPLINE_FIT =4 # Spline-fit vertices have been added -POLYLINE_3D =8 # This is a 3D polyline -POLYGON_MESH =16 # This is a 3D polygon mesh -CLOSED_N =32 # The polygon mesh is closed in the N direction -POLYFACE_MESH =64 # The polyline is a polyface mesh -CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline - -#---text flags -#horizontal -LEFT = 0 -CENTER = 1 -RIGHT = 2 -ALIGNED = 3 #if vertical alignment = 0 -MIDDLE = 4 #if vertical alignment = 0 -FIT = 5 #if vertical alignment = 0 -#vertical -BASELINE = 0 -BOTTOM = 1 -MIDDLE = 2 -TOP = 3 - -####3) Classes -#---entitities ----------------------------------------------- -#-------------------------- -class Arc(_Entity): - """Arc, angles in degrees.""" - def __init__(self,center=(0,0,0),radius=1, - startAngle=0.0,endAngle=90,**common): - """Angles in degrees.""" - _Entity.__init__(self,**common) - self.center=center - self.radius=radius - self.startAngle=startAngle - self.endAngle=endAngle - def __str__(self): - return ' 0\nARC\n%s%s\n 40\n%s\n 50\n%s\n 51\n%s\n'%\ - (self._common(),_point(self.center), - self.radius,self.startAngle,self.endAngle) - -#----------------------------------------------- -class Circle(_Entity): - """Circle""" - def __init__(self,center=(0,0,0),radius=1,**common): - _Entity.__init__(self,**common) - self.center=center - self.radius=radius - def __str__(self): - return ' 0\nCIRCLE\n%s%s\n 40\n%s\n'%\ - (self._common(),_point(self.center),self.radius) - -#----------------------------------------------- -class Face(_Entity): - """3dface""" - def __init__(self,points,**common): - _Entity.__init__(self,**common) - while len(points)<4: #fix for r12 format - points.append(points[-1]) - self.points=points - - def __str__(self): - out = ' 0\n3DFACE\n%s%s\n' %(self._common(),_points(self.points)) - #print 'deb:out=', out #------------------- - return out - -#----------------------------------------------- -class Insert(_Entity): - """Block instance.""" - def __init__(self,name,point=(0,0,0), - xscale=None,yscale=None,zscale=None, - cols=None,colspacing=None,rows=None,rowspacing=None, - rotation=None, - **common): - _Entity.__init__(self,**common) - self.name=name - self.point=point - self.xscale=xscale - self.yscale=yscale - self.zscale=zscale - self.cols=cols - self.colspacing=colspacing - self.rows=rows - self.rowspacing=rowspacing - self.rotation=rotation - - def __str__(self): - result=' 0\nINSERT\n 2\n%s\n%s%s\n'%\ - (self.name,self._common(),_point(self.point)) - if self.xscale!=None:result+=' 41\n%s\n'%self.xscale - if self.yscale!=None:result+=' 42\n%s\n'%self.yscale - if self.zscale!=None:result+=' 43\n%s\n'%self.zscale - if self.rotation:result+=' 50\n%s\n'%self.rotation - if self.cols!=None:result+=' 70\n%s\n'%self.cols - if self.colspacing!=None:result+=' 44\n%s\n'%self.colspacing - if self.rows!=None:result+=' 71\n%s\n'%self.rows - if self.rowspacing!=None:result+=' 45\n%s\n'%self.rowspacing - return result - -#----------------------------------------------- -class Line(_Entity): - """Line""" - def __init__(self,points,**common): - _Entity.__init__(self,**common) - self.points=points - def __str__(self): - return ' 0\nLINE\n%s%s\n' %( - self._common(), _points(self.points)) - - -#----------------------------------------------- -class PolyLine(_Entity): - def __init__(self,points,org_point=[0,0,0],flag=0,width=None,**common): - #width = number, or width = list [width_start=None, width_end=None] - #for 2d-polyline: points = [ [x, y, z, width_start=None, width_end=None, bulge=0 or None], ...] - #for 3d-polyline: points = [ [x, y, z], ...] - #for polyface: points = [points_list, faces_list] - _Entity.__init__(self,**common) - self.points=points - self.org_point=org_point - self.flag=flag - self.polyface = False - self.polyline2d = False - self.faces = [] # dummy value - self.width= None # dummy value - if self.flag & POLYFACE_MESH: - self.polyface=True - self.points=points[0] - self.faces=points[1] - self.p_count=len(self.points) - self.f_count=len(self.faces) - elif not self.flag & POLYLINE_3D: - self.polyline2d = True - if width: - if type(width)!='list': - width=[width,width] - self.width=width - - def __str__(self): - result= ' 0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.flag) - result+=' 66\n1\n' - result+='%s\n' %_point(self.org_point) - if self.polyface: - result+=' 71\n%s\n' %self.p_count - result+=' 72\n%s\n' %self.f_count - elif self.polyline2d: - if self.width!=None: result+=' 40\n%s\n 41\n%s\n' %(self.width[0],self.width[1]) - for point in self.points: - result+=' 0\nVERTEX\n' - result+=' 8\n%s\n' %self.layer - if self.polyface: - result+='%s\n' %_point(point[0:3]) - result+=' 70\n192\n' - elif self.polyline2d: - result+='%s\n' %_point(point[0:2]) - if len(point)>4: - width1, width2 = point[3], point[4] - if width1!=None: result+=' 40\n%s\n' %width1 - if width2!=None: result+=' 41\n%s\n' %width2 - if len(point)==6: - bulge = point[5] - if bulge: result+=' 42\n%s\n' %bulge - else: - result+='%s\n' %_point(point[0:3]) - for face in self.faces: - result+=' 0\nVERTEX\n' - result+=' 8\n%s\n' %self.layer - result+='%s\n' %_point(self.org_point) - result+=' 70\n128\n' - result+=' 71\n%s\n' %face[0] - result+=' 72\n%s\n' %face[1] - result+=' 73\n%s\n' %face[2] - if len(face)==4: result+=' 74\n%s\n' %face[3] - result+=' 0\nSEQEND\n' - result+=' 8\n%s\n' %self.layer - return result - -#----------------------------------------------- -class Point(_Entity): - """Point.""" - def __init__(self,points=None,**common): - _Entity.__init__(self,**common) - self.points=points - def __str__(self): # TODO: - return ' 0\nPOINT\n%s%s\n' %(self._common(), - _points(self.points) - ) - -#----------------------------------------------- -class Solid(_Entity): - """Colored solid fill.""" - def __init__(self,points=None,**common): - _Entity.__init__(self,**common) - self.points=points - def __str__(self): - return ' 0\nSOLID\n%s%s\n' %(self._common(), - _points(self.points[:2]+[self.points[3],self.points[2]]) - ) - - -#----------------------------------------------- -class Dimension(_Entity): - """Basic dimension entity""" - def __init__(self,point,start,end,**common): - _Entity.__init__(self,**common) - self.points=[point,start,end] - def __str__(self): - result = ' 0\nDIMENSION\n%s' %(self._common()) - result+=' 3\nStandard\n' - result+=' 70\n1\n' - result+='%s\n' %_point(self.points[0]) - result+='%s\n' %_point(self.points[1],3) - result+='%s\n' %_point(self.points[2],4) - print result - return result - -#----------------------------------------------- -class Text(_Entity): - """Single text line.""" - def __init__(self,text='',point=(0,0,0),alignment=None, - flag=None,height=1,justifyhor=None,justifyver=None, - rotation=None,obliqueAngle=None,style=None,xscale=None,**common): - _Entity.__init__(self,**common) - self.text=text - self.point=point - self.alignment=alignment - self.flag=flag - self.height=height - self.justifyhor=justifyhor - self.justifyver=justifyver - self.rotation=rotation - self.obliqueAngle=obliqueAngle - self.style=style - self.xscale=xscale - def __str__(self): - result= ' 0\nTEXT\n%s%s\n 40\n%s\n 1\n%s\n'%\ - (self._common(),_point(self.point),self.height,self.text) - if self.rotation: result+=' 50\n%s\n'%self.rotation - if self.xscale: result+=' 41\n%s\n'%self.xscale - if self.obliqueAngle: result+=' 51\n%s\n'%self.obliqueAngle - if self.style: result+=' 7\n%s\n'%self.style - if self.flag: result+=' 71\n%s\n'%self.flag - if self.justifyhor: result+=' 72\n%s\n'%self.justifyhor - if self.alignment: result+='%s\n'%_point(self.alignment,1) - if self.justifyver: result+=' 73\n%s\n'%self.justifyver - return result - -#----------------------------------------------- -class Mtext(Text): - """Surrogate for mtext, generates some Text instances.""" - def __init__(self,text='',point=(0,0,0),width=250,spacingFactor=1.5,down=0,spacingWidth=None,**options): - Text.__init__(self,text=text,point=point,**options) - if down:spacingFactor*=-1 - self.spacingFactor=spacingFactor - self.spacingWidth=spacingWidth - self.width=width - self.down=down - def __str__(self): - texts=self.text.replace('\r\n','\n').split('\n') - if not self.down:texts.reverse() - result='' - x=y=0 - if self.spacingWidth:spacingWidth=self.spacingWidth - else:spacingWidth=self.height*self.spacingFactor - for text in texts: - while text: - result+='%s\n'%Text(text[:self.width], - point=(self.point[0]+x*spacingWidth, - self.point[1]+y*spacingWidth, - self.point[2]), - alignment=self.alignment,flag=self.flag,height=self.height, - justifyhor=self.justifyhor,justifyver=self.justifyver, - rotation=self.rotation,obliqueAngle=self.obliqueAngle, - style=self.style,xscale=self.xscale,parent=self - ) - text=text[self.width:] - if self.rotation:x+=1 - else:y+=1 - return result[1:] - -#----------------------------------------------- -##class _Mtext(_Entity): -## """Mtext not functioning for minimal dxf.""" -## def __init__(self,text='',point=(0,0,0),attachment=1, -## charWidth=None,charHeight=1,direction=1,height=100,rotation=0, -## spacingStyle=None,spacingFactor=None,style=None,width=100, -## xdirection=None,**common): -## _Entity.__init__(self,**common) -## self.text=text -## self.point=point -## self.attachment=attachment -## self.charWidth=charWidth -## self.charHeight=charHeight -## self.direction=direction -## self.height=height -## self.rotation=rotation -## self.spacingStyle=spacingStyle -## self.spacingFactor=spacingFactor -## self.style=style -## self.width=width -## self.xdirection=xdirection -## def __str__(self): -## input=self.text -## text='' -## while len(input)>250: -## text+='3\n%s\n'%input[:250] -## input=input[250:] -## text+='1\n%s\n'%input -## result= '0\nMTEXT\n%s\n%s\n40\n%s\n41\n%s\n71\n%s\n72\n%s%s\n43\n%s\n50\n%s\n'%\ -## (self._common(),_point(self.point),self.charHeight,self.width, -## self.attachment,self.direction,text, -## self.height, -## self.rotation) -## if self.style:result+='7\n%s\n'%self.style -## if self.xdirection:result+='%s\n'%_point(self.xdirection,1) -## if self.charWidth:result+='42\n%s\n'%self.charWidth -## if self.spacingStyle:result+='73\n%s\n'%self.spacingStyle -## if self.spacingFactor:result+='44\n%s\n'%self.spacingFactor -## return result - -#---tables --------------------------------------------------- -#----------------------------------------------- -class Block(_Collection): - """Use list methods to add entities, eg append.""" - def __init__(self,name,layer='0',flag=0,base=(0,0,0),entities=[]): - self.entities=copy.copy(entities) - _Collection.__init__(self,entities) - self.layer=layer - self.name=name - self.flag=0 - self.base=base - def __str__(self): # TODO: - e=''.join([str(x)for x in self.entities]) - return ' 0\nBLOCK\n 8\n%s\n 2\n%s\n 70\n%s\n%s\n 3\n%s\n%s 0\nENDBLK\n'%\ - (self.layer,self.name.upper(),self.flag,_point(self.base),self.name.upper(),e) - -#----------------------------------------------- -class Layer(_Call): - """Layer""" - def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64): - self.name=name - self.color=color - self.lineType=lineType - self.flag=flag - def __str__(self): - return ' 0\nLAYER\n 2\n%s\n 70\n%s\n 62\n%s\n 6\n%s\n'%\ - (self.name.upper(),self.flag,self.color,self.lineType) - -#----------------------------------------------- -class LineType(_Call): - """Custom linetype""" - def __init__(self,name='CONTINUOUS',description='Solid line',elements=[0.0],flag=0): - self.name=name - self.description=description - self.elements=copy.copy(elements) - self.flag=flag - def __str__(self): - result = ' 0\nLTYPE\n 2\n%s\n 70\n%s\n 3\n%s\n 72\n65\n'%\ - (self.name.upper(),self.flag,self.description) - if self.elements: - elements = ' 73\n%s\n' %(len(self.elements)-1) - elements += ' 40\n%s\n' %(self.elements[0]) - for e in self.elements[1:]: - elements += ' 49\n%s\n' %e - result += elements - return result - - -#----------------------------------------------- -class Style(_Call): - """Text style""" - def __init__(self,name='standard',flag=0,height=0,widthFactor=1.0,obliqueAngle=0.0, - mirror=0,lastHeight=1,font='arial.ttf',bigFont=''): - self.name=name - self.flag=flag - self.height=height - self.widthFactor=widthFactor - self.obliqueAngle=obliqueAngle - self.mirror=mirror - self.lastHeight=lastHeight - self.font=font - self.bigFont=bigFont - def __str__(self): - return ' 0\nSTYLE\n 2\n%s\n 70\n%s\n 40\n%s\n 41\n%s\n 50\n%s\n 71\n%s\n 42\n%s\n 3\n%s\n 4\n%s\n'%\ - (self.name.upper(),self.flag,self.flag,self.widthFactor, - self.obliqueAngle,self.mirror,self.lastHeight, - self.font.upper(),self.bigFont.upper()) - -#----------------------------------------------- -class VPort(_Call): - def __init__(self,name,flag=0, - leftBottom=(0.0,0.0), - rightTop=(1.0,1.0), - center=(0.5,0.5), - snap_base=(0.0,0.0), - snap_spacing=(0.1,0.1), - grid_spacing=(0.1,0.1), - direction=(0.0,0.0,1.0), - target=(0.0,0.0,0.0), - height=1.0, - ratio=1.0, - lens=50, - frontClipping=0, - backClipping=0, - snap_rotation=0, - twist=0, - mode=0, - circle_zoom=100, - fast_zoom=1, - ucsicon=1, - snap_on=0, - grid_on=0, - snap_style=0, - snap_isopair=0 - ): - self.name=name - self.flag=flag - self.leftBottom=leftBottom - self.rightTop=rightTop - self.center=center - self.snap_base=snap_base - self.snap_spacing=snap_spacing - self.grid_spacing=grid_spacing - self.direction=direction - self.target=target - self.height=float(height) - self.ratio=float(ratio) - self.lens=float(lens) - self.frontClipping=float(frontClipping) - self.backClipping=float(backClipping) - self.snap_rotation=float(snap_rotation) - self.twist=float(twist) - self.mode=mode - self.circle_zoom=circle_zoom - self.fast_zoom=fast_zoom - self.ucsicon=ucsicon - self.snap_on=snap_on - self.grid_on=grid_on - self.snap_style=snap_style - self.snap_isopair=snap_isopair - def __str__(self): - output = [' 0', 'VPORT', - ' 2', self.name, - ' 70', self.flag, - _point(self.leftBottom), - _point(self.rightTop,1), - _point(self.center,2), # View center point (in DCS) - _point(self.snap_base,3), - _point(self.snap_spacing,4), - _point(self.grid_spacing,5), - _point(self.direction,6), #view direction from target (in WCS) - _point(self.target,7), - ' 40', self.height, - ' 41', self.ratio, - ' 42', self.lens, - ' 43', self.frontClipping, - ' 44', self.backClipping, - ' 50', self.snap_rotation, - ' 51', self.twist, - ' 71', self.mode, - ' 72', self.circle_zoom, - ' 73', self.fast_zoom, - ' 74', self.ucsicon, - ' 75', self.snap_on, - ' 76', self.grid_on, - ' 77', self.snap_style, - ' 78', self.snap_isopair - ] - - output_str = '' - for s in output: - output_str += '%s\n' %s - return output_str - - - -#----------------------------------------------- -class View(_Call): - def __init__(self,name,flag=0, - width=1, - height=1, - center=(0.5,0.5), - direction=(0,0,1), - target=(0,0,0), - lens=50, - frontClipping=0, - backClipping=0, - twist=0,mode=0 - ): - self.name=name - self.flag=flag - self.width=float(width) - self.height=float(height) - self.center=center - self.direction=direction - self.target=target - self.lens=float(lens) - self.frontClipping=float(frontClipping) - self.backClipping=float(backClipping) - self.twist=float(twist) - self.mode=mode - def __str__(self): - output = [' 0', 'VIEW', - ' 2', self.name, - ' 70', self.flag, - ' 40', self.height, - _point(self.center), - ' 41', self.width, - _point(self.direction,1), - _point(self.target,2), - ' 42', self.lens, - ' 43', self.frontClipping, - ' 44', self.backClipping, - ' 50', self.twist, - ' 71', self.mode - ] - output_str = '' - for s in output: - output_str += '%s\n' %s - return output_str - -#----------------------------------------------- -def ViewByWindow(name,leftBottom=(0,0),rightTop=(1,1),**options): - width=abs(rightTop[0]-leftBottom[0]) - height=abs(rightTop[1]-leftBottom[1]) - center=((rightTop[0]+leftBottom[0])*0.5,(rightTop[1]+leftBottom[1])*0.5) - return View(name=name,width=width,height=height,center=center,**options) - -#---drawing -#----------------------------------------------- -class Drawing(_Collection): - """Dxf drawing. Use append or any other list methods to add objects.""" - def __init__(self,insbase=(0.0,0.0,0.0),extmin=(0.0,0.0,0.0),extmax=(0.0,0.0,0.0), - layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[], - views=[],vports=[],entities=None,fileName='test.dxf'): - # TODO: replace list with None,arial - if not entities: - entities=[] - _Collection.__init__(self,entities) - self.insbase=insbase - self.extmin=extmin - self.extmax=extmax - self.layers=copy.copy(layers) - self.linetypes=copy.copy(linetypes) - self.styles=copy.copy(styles) - self.views=copy.copy(views) - self.vports=copy.copy(vports) - self.blocks=copy.copy(blocks) - self.fileName=fileName - #private - #self.acadver='9\n$ACADVER\n1\nAC1006\n' - self.acadver=' 9\n$ACADVER\n 1\nAC1009\n' - """DXF AutoCAD-Release format codes - AC1021 2008, 2007 - AC1018 2006, 2005, 2004 - AC1015 2002, 2000i, 2000 - AC1014 R14,14.01 - AC1012 R13 - AC1009 R12,11 - AC1006 R10 - AC1004 R9 - AC1002 R2.6 - AC1.50 R2.05 - """ - - def _name(self,x): - """Helper function for self._point""" - return ' 9\n$%s\n' %x.upper() - - def _point(self,name,x): - """Point setting from drawing like extmin,extmax,...""" - return '%s%s' %(self._name(name),_point(x)) - - def _section(self,name,x): - """Sections like tables,blocks,entities,...""" - if x: xstr=''.join(x) - else: xstr='' - return ' 0\nSECTION\n 2\n%s\n%s 0\nENDSEC\n'%(name.upper(),xstr) - - def _table(self,name,x): - """Tables like ltype,layer,style,...""" - if x: xstr=''.join(x) - else: xstr='' - return ' 0\nTABLE\n 2\n%s\n 70\n%s\n%s 0\nENDTAB\n'%(name.upper(),len(x),xstr) - - def __str__(self): - """Returns drawing as dxf string.""" - header=[self.acadver]+[self._point(attr,getattr(self,attr))+'\n' for attr in _HEADER_POINTS] - header=self._section('header',header) - - tables=[self._table('vport',[str(x) for x in self.vports]), - self._table('ltype',[str(x) for x in self.linetypes]), - self._table('layer',[str(x) for x in self.layers]), - self._table('style',[str(x) for x in self.styles]), - self._table('view',[str(x) for x in self.views]), - ] - tables=self._section('tables',tables) - - blocks=self._section('blocks',[str(x) for x in self.blocks]) - - entities=self._section('entities',[str(x) for x in self.entities]) - - all=''.join([header,tables,blocks,entities,' 0\nEOF\n']) - return all - - def saveas(self,fileName): - self.fileName=fileName - self.save() - - def save(self): - test=open(self.fileName,'w') - test.write(str(self)) - test.close() - - -#---extras -#----------------------------------------------- -class Rectangle(_Entity): - """Rectangle, creates lines.""" - def __init__(self,point=(0,0,0),width=1,height=1,solid=None,line=1,**common): - _Entity.__init__(self,**common) - self.point=point - self.width=width - self.height=height - self.solid=solid - self.line=line - def __str__(self): - result='' - points=[self.point,(self.point[0]+self.width,self.point[1],self.point[2]), - (self.point[0]+self.width,self.point[1]+self.height,self.point[2]), - (self.point[0],self.point[1]+self.height,self.point[2]),self.point] - if self.solid: - result+= Solid(points=points[:-1],parent=self.solid) - if self.line: - for i in range(4): - result+= Line(points=[points[i],points[i+1]],parent=self) - return result[1:] - -#----------------------------------------------- -class LineList(_Entity): - """Like polyline, but built of individual lines.""" - def __init__(self,points=[],org_point=[0,0,0],closed=0,**common): - _Entity.__init__(self,**common) - self.closed=closed - self.points=copy.copy(points) - def __str__(self): - if self.closed:points=self.points+[self.points[0]] - else: points=self.points - result='' - for i in range(len(points)-1): - result+= Line(points=[points[i],points[i+1]],parent=self) - return result[1:] - -#----------------------------------------------------- -def test(): - #Blocks - b=Block('test') - b.append(Solid(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=1)) - b.append(Arc(center=(1,0,0),color=2)) - - #Drawing - d=Drawing() - #tables - d.blocks.append(b) #table blocks - d.styles.append(Style()) #table styles - d.views.append(View('Normal')) #table view - d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1))) #idem - - #entities - d.append(Circle(center=(1,1,0),color=3)) - d.append(Face(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=4)) - d.append(Insert('test',point=(3,3,3),cols=5,colspacing=2)) - d.append(Line(points=[(0,0,0),(1,1,1)])) - d.append(Mtext('Click on Ads\nmultiple lines with mtext',point=(1,1,1),color=5,rotation=90)) - d.append(Text('Please donate!',point=(3,0,1))) - #d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2))) - d.append(Solid(points=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color=3)) - #d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],flag=1,color=1)) - - #d.saveas('c:\\test.dxf') - d.saveas('test.dxf') - -#----------------------------------------------------- -if __name__=='__main__': - if not copy: - Draw.PupMenu('Error%t|This script requires a full python install') - else: test() - diff --git a/src/Mod/Draft/draftlibs/dxfReader.py b/src/Mod/Draft/draftlibs/dxfReader.py deleted file mode 100644 index e03777515..000000000 --- a/src/Mod/Draft/draftlibs/dxfReader.py +++ /dev/null @@ -1,384 +0,0 @@ -"""This module provides a function for reading dxf files and parsing them into a useful tree of objects and data. - - The convert function is called by the readDXF fuction to convert dxf strings into the correct data based - on their type code. readDXF expects a (full path) file name as input. -""" - -# -------------------------------------------------------------------------- -# DXF Reader v0.9 by Ed Blake (AKA Kitsu) -# 2008.05.08 modif.def convert() by Remigiusz Fiedler (AKA migius) -# -------------------------------------------------------------------------- -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** -# -------------------------------------------------------------------------- - - -from dxfImportObjects import * - -class Object: - """Empty container class for dxf objects""" - - def __init__(self, _type='', block=False): - """_type expects a string value.""" - self.type = _type - self.name = '' - self.data = [] - - def __str__(self): - if self.name: - return self.name - else: - return self.type - - def __repr__(self): - return str(self.data) - - def get_type(self, kind=''): - """Despite the name, this method actually returns all objects of type 'kind' from self.data.""" - if type: - objects = [] - for item in self.data: - if type(item) != list and item.type == kind: - # we want this type of object - objects.append(item) - elif type(item) == list and item[0] == kind: - # we want this type of data - objects.append(item[1]) - return objects - - -class InitializationError(Exception): pass - -class StateMachine: - """(finite) State Machine from the great David Mertz's great Charming Python article.""" - - def __init__(self): - self.handlers = [] - self.startState = None - self.endStates = [] - - def add_state(self, handler, end_state=0): - """All states and handlers are functions which return - a state and a cargo.""" - self.handlers.append(handler) - if end_state: - self.endStates.append(handler) - def set_start(self, handler): - """Sets the starting handler function.""" - self.startState = handler - - - def run(self, cargo=None): - if not self.startState: - raise InitializationError,\ - "must call .set_start() before .run()" - if not self.endStates: - raise InitializationError, \ - "at least one state must be an end_state" - handler = self.startState - while 1: - (newState, cargo) = handler(cargo) - #print cargo - if newState in self.endStates: - return newState(cargo) - #break - elif newState not in self.handlers: - raise RuntimeError, "Invalid target %s" % newState - else: - handler = newState - -def get_name(data): - """Get the name of an object from its object data. - - Returns a pair of (data_item, name) where data_item is the list entry where the name was found - (the data_item can be used to remove the entry from the object data). Be sure to check - name not None before using the returned values! - """ - value = None - for item in data: - if item[0] == 2: - value = item[1] - break - return item, value - -def get_layer(data): - """Expects object data as input. - - Returns (entry, layer_name) where entry is the data item that provided the layer name. - """ - value = None - for item in data: - if item[0] == 8: - value = item[1] - break - return item, value - - -def convert(code, value): - """Convert a string to the correct Python type based on its dxf code. - code types: - ints = 60-79, 170-179, 270-289, 370-389, 400-409, 1060-1070 - longs = 90-99, 420-429, 440-459, 1071 - floats = 10-39, 40-59, 110-139, 140-149, 210-239, 460-469, 1010-1059 - hex = 105, 310-379, 390-399 - strings = 0-9, 100, 102, 300-309, 410-419, 430-439, 470-479, 999, 1000-1009 - """ - if 59 < code < 80 or 169 < code < 180 or 269 < code < 290 or 369 < code < 390 or 399 < code < 410 or 1059 < code < 1071: - value = int(float(value)) - elif 89 < code < 100 or 419 < code < 430 or 439 < code < 460 or code == 1071: - value = long(float(value)) - elif 9 < code < 60 or 109 < code < 150 or 209 < code < 240 or 459 < code < 470 or 1009 < code < 1060: - value = float(value) - elif code == 105 or 309 < code < 380 or 389 < code < 400: - try: - value = int(value, 16) # should be left as string? - except: - pass - else: # it's already a string so do nothing - pass - return value - - -def findObject(infile, kind=''): - """Finds the next occurance of an object.""" - obj = False - while 1: - line = infile.readline() - if not line: # readline returns '' at eof - return False - if not obj: # We're still looking for our object code - if line.lower().strip() == '0': - obj = True # found it - else: # we are in an object definition - if kind: # if we're looking for a particular kind - if line.lower().strip() == kind: - obj = Object(line.lower().strip()) - break - else: # otherwise take anything non-numeric - if line.lower().strip() not in string.digits: - obj = Object(line.lower().strip()) - break - obj = False # whether we found one or not it's time to start over - return obj - -def handleObject(infile): - """Add data to an object until end of object is found.""" - line = infile.readline() - if line.lower().strip() == 'section': - return 'section' # this would be a problem - elif line.lower().strip() == 'endsec': - return 'endsec' # this means we are done with a section - else: # add data to the object until we find a new object - obj = Object(line.lower().strip()) - obj.name = obj.type - done = False - data = [] - while not done: - line = infile.readline() - if not data: - if line.lower().strip() == '0': - #we've found an object, time to return - return obj - else: - # first part is always an int - data.append(int(line.lower().strip())) - else: - data.append(convert(data[0], line.strip())) - obj.data.append(data) - data = [] - -def handleTable(table, infile): - """Special handler for dealing with nested table objects.""" - item, name = get_name(table.data) - if name: # We should always find a name - table.data.remove(item) - table.name = name.lower() - # This next bit is from handleObject - # handleObject should be generalized to work with any section like object - while 1: - obj = handleObject(infile) - if obj.type == 'table': - print "Warning: previous table not closed!" - return table - elif obj.type == 'endtab': - return table # this means we are done with the table - else: # add objects to the table until one of the above is found - table.data.append(obj) - - - - -def handleBlock(block, infile): - """Special handler for dealing with nested table objects.""" - item, name = get_name(block.data) - if name: # We should always find a name - # block.data.remove(item) - block.name = name - # This next bit is from handleObject - # handleObject should be generalized to work with any section like object - while 1: - obj = handleObject(infile) - if obj.type == 'block': - print "Warning: previous block not closed!" - return block - elif obj.type == 'endblk': - return block # this means we are done with the table - else: # add objects to the table until one of the above is found - block.data.append(obj) - - - - -"""These are the states/functions used in the State Machine. -states: - start - find first section - start_section - add data, find first object - object - add obj-data, watch for next obj (called directly by start_section) - end_section - look for next section or eof - end - return results -""" - -def start(cargo): - """Expects the infile as cargo, initializes the cargo.""" - #print "Entering start state!" - infile = cargo - drawing = Object('drawing') - section = findObject(infile, 'section') - if section: - return start_section, (infile, drawing, section) - else: - return error, (infile, "Failed to find any sections!") - -def start_section(cargo): - """Expects [infile, drawing, section] as cargo, builds a nested section object.""" - #print "Entering start_section state!" - infile = cargo[0] - drawing = cargo[1] - section = cargo[2] - # read each line, if it is an object declaration go to object mode - # otherwise create a [index, data] pair and add it to the sections data. - done = False - data = [] - while not done: - line = infile.readline() - - if not data: # if we haven't found a dxf code yet - if line.lower().strip() == '0': - # we've found an object - while 1: # no way out unless we find an end section or a new section - obj = handleObject(infile) - if obj == 'section': # shouldn't happen - print "Warning: failed to close previous section!" - return end_section, (infile, drawing) - elif obj == 'endsec': # This section is over, look for the next - drawing.data.append(section) - return end_section, (infile, drawing) - elif obj.type == 'table': # tables are collections of data - obj = handleTable(obj, infile) # we need to find all there contents - section.data.append(obj) # before moving on - elif obj.type == 'block': # the same is true of blocks - obj = handleBlock(obj, infile) # we need to find all there contents - section.data.append(obj) # before moving on - else: # found another sub-object - section.data.append(obj) - else: - data.append(int(line.lower().strip())) - else: # we have our code, now we just need to convert the data and add it to our list. - data.append(convert(data[0], line.strip())) - section.data.append(data) - data = [] -def end_section(cargo): - """Expects (infile, drawing) as cargo, searches for next section.""" - #print "Entering end_section state!" - infile = cargo[0] - drawing = cargo[1] - section = findObject(infile, 'section') - if section: - return start_section, (infile, drawing, section) - else: - return end, (infile, drawing) - -def end(cargo): - """Expects (infile, drawing) as cargo, called when eof has been reached.""" - #print "Entering end state!" - infile = cargo[0] - drawing = cargo[1] - #infile.close() - return drawing - -def error(cargo): - """Expects a (infile, string) as cargo, called when there is an error during processing.""" - #print "Entering error state!" - infile = cargo[0] - err = cargo[1] - infile.close() - print "There has been an error:" - print err - return False - -def readDXF(filename): - """Given a file name try to read it as a dxf file. - - Output is an object with the following structure - drawing - header - header data - classes - class data - tables - table data - blocks - block data - entities - entity data - objects - object data - where foo data is a list of sub-objects. True object data - is of the form [code, data]. -""" - infile = open(filename) - - sm = StateMachine() - sm.add_state(error, True) - sm.add_state(end, True) - sm.add_state(start_section) - sm.add_state(end_section) - sm.add_state(start) - sm.set_start(start) - try: - drawing = sm.run(infile) - if drawing: - drawing.name = filename - for obj in drawing.data: - item, name = get_name(obj.data) - if name: - obj.data.remove(item) - obj.name = name.lower() - setattr(drawing, name.lower(), obj) - # Call the objectify function to cast - # raw objects into the right types of object - obj.data = objectify(obj.data) - #print obj.name - finally: - infile.close() - return drawing -if __name__ == "__main__": - filename = r".\examples\block-test.dxf" - drawing = readDXF(filename) - for item in drawing.entities.data: - print item diff --git a/src/Mod/Draft/importDWG.py b/src/Mod/Draft/importDWG.py index bbfc947a0..f976b1a01 100644 --- a/src/Mod/Draft/importDWG.py +++ b/src/Mod/Draft/importDWG.py @@ -5,7 +5,7 @@ #* Copyright (c) 2009 Yorik van Havre * #* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (GPL) * +#* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index 7c19cc0a5..494c5166e 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -1,4 +1,3 @@ - # -*- coding: utf8 -*- #*************************************************************************** @@ -6,7 +5,7 @@ #* Copyright (c) 2009 Yorik van Havre * #* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (GPL) * +#* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * @@ -40,21 +39,33 @@ lines, polylines, lwpolylines, circles, arcs, texts, colors,layers (from groups) ''' -import FreeCAD, os, Part, math, re, string, Mesh, Draft, DraftVecUtils, DraftGeomUtils -from draftlibs import dxfColorMap, dxfLibrary -from draftlibs.dxfReader import readDXF +import sys, FreeCAD, os, Part, math, re, string, Mesh, Draft, DraftVecUtils, DraftGeomUtils from Draft import _Dimension, _ViewProviderDimension from FreeCAD import Vector -try: import FreeCADGui -except: gui = False -else: gui = True -try: draftui = FreeCADGui.draftToolBar -except: draftui = None +gui = FreeCAD.GuiUp +try: + draftui = FreeCADGui.draftToolBar +except: + draftui = None + +files = ['dxfColorMap.py','dxfImportObjects.py','dxfLibrary.py','dxfReader.py'] +baseurl = 'https://raw.github.com/yorikvanhavre/Draft-dxf-importer/master/' +for f in files: + p = os.path.join(FreeCAD.ConfigGet("UserAppData"),f) + if not os.path.exists(p): + import ArchCommands + p = None + p = ArchCommands.download(baseurl+f) + if not p: + FreeCAD.Console.PrintWarning("Download of dxf libraries failed. Please download them manually from https://github.com/yorikvanhavre/Draft-dxf-importer") + sys.exit() +sys.path.append(FreeCAD.ConfigGet("UserAppData")) +import dxfColorMap, dxfLibrary, dxfReader if open.__module__ == '__builtin__': pythonopen = open # to distinguish python built-in open function from the one declared here - + def prec(): "returns the current Draft precision level" return Draft.getParam("precision") @@ -140,10 +151,10 @@ def calcBulge(v1,bulge,v2): endpoint = DraftVecUtils.scale(perp,sagitta) return startpoint.add(endpoint) -def getGroup(ob,exportList): +def getGroup(ob): "checks if the object is part of a group" - for i in exportList: - if (i.Type == "App::DocumentObjectGroup"): + for i in FreeCAD.ActiveDocument.Objects: + if (i.TypeId == "App::DocumentObjectGroup"): for j in i.Group: if (j == ob): return i.Label @@ -151,7 +162,8 @@ def getGroup(ob,exportList): def getACI(ob,text=False): "gets the ACI color closest to the objects color" - if not gui: return 0 + if not gui: + return 0 else: if text: col=ob.ViewObject.TextColor @@ -772,7 +784,7 @@ def processdxf(document,filename): "this does the translation of the dxf contents into FreeCAD Part objects" global drawing # for debugging - so drawing is still accessible to python after the script FreeCAD.Console.PrintMessage("opening "+filename+"...\n") - drawing = readDXF(filename) + drawing = dxfReader.readDXF(filename) global layers layers = [] global doc @@ -1326,7 +1338,7 @@ def getWire(wire,nospline=False): def getBlock(sh,obj): "returns a dxf block with the contents of the object" - block = dxfLibrary.Block(name=obj.Name,layer=getGroup(obj,exportList)) + block = dxfLibrary.Block(name=obj.Name,layer=getGroup(obj)) writeShape(sh,obj,block) return block @@ -1341,15 +1353,15 @@ def writeShape(sh,ob,dxfobject,nospline=False): if len(wire.Edges[0].Vertexes) == 1: # circle dxfobject.append(dxfLibrary.Circle(center, radius, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) else: # arc dxfobject.append(dxfLibrary.Arc(center, radius, ang1, ang2, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) else: dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline), [0.0,0.0,0.0], int(DraftGeomUtils.isReallyClosed(wire)), color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) if len(processededges) < len(sh.Edges): # lone edges loneedges = [] for e in sh.Edges: @@ -1363,7 +1375,7 @@ def writeShape(sh,ob,dxfobject,nospline=False): if c: dxfobject.append(dxfLibrary.Circle(DraftVecUtils.tup(c.Curve.Center), c.Curve.Radius, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) else: points = [] spline = getSplineSegs(edge) @@ -1371,7 +1383,7 @@ def writeShape(sh,ob,dxfobject,nospline=False): points.append((p.x,p.y,p.z,None,None,0.0)) dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0], 0, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) elif DraftGeomUtils.geomType(edge) == "Circle": # curves center, radius, ang1, ang2 = getArcData(edge) if not isinstance(center,tuple): @@ -1379,18 +1391,18 @@ def writeShape(sh,ob,dxfobject,nospline=False): if len(edge.Vertexes) == 1: # circles dxfobject.append(dxfLibrary.Circle(center, radius, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) else : # arcs dxfobject.append(dxfLibrary.Arc(center, radius, ang1, ang2, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) else: # anything else is treated as lines if len(edge.Vertexes) > 1: ve1=edge.Vertexes[0].Point ve2=edge.Vertexes[1].Point dxfobject.append(dxfLibrary.Line([DraftVecUtils.tup(ve1), DraftVecUtils.tup(ve2)], color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) def writeMesh(ob,dxfobject): "export a shape as a polyface mesh" @@ -1405,12 +1417,14 @@ def writeMesh(ob,dxfobject): # print len(points),len(faces) dxfobject.append(dxfLibrary.PolyLine([points,faces], [0.0,0.0,0.0], 64, color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) def export(objectslist,filename,nospline=False): "called when freecad exports a file. If nospline=True, bsplines are exported as straight segs" global exportList exportList = objectslist + + exportList = Draft.getGroupContents(exportList) if (len(exportList) == 1) and (Draft.getType(exportList[0]) == "ArchSectionView"): # arch view: export it "as is" @@ -1476,7 +1490,7 @@ def export(objectslist,filename,nospline=False): dxf.append(dxfLibrary.Text(text,point,height=height, color=getACI(ob,text=True), style='STANDARD', - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) elif Draft.getType(ob) == "Dimension": p1 = DraftVecUtils.tup(ob.Start) @@ -1488,7 +1502,7 @@ def export(objectslist,filename,nospline=False): else: pbase = DraftVecUtils.tup(ob.End.add(DraftVecUtils.neg(proj))) dxf.append(dxfLibrary.Dimension(pbase,p1,p2,color=getACI(ob), - layer=getGroup(ob,exportList))) + layer=getGroup(ob))) dxf.saveas(filename) FreeCAD.Console.PrintMessage("successfully exported "+filename+"\r\n") diff --git a/src/Mod/Draft/importOCA.py b/src/Mod/Draft/importOCA.py index e71fa060e..d618258ee 100644 --- a/src/Mod/Draft/importOCA.py +++ b/src/Mod/Draft/importOCA.py @@ -1,10 +1,11 @@ +# -*- coding: utf8 -*- #*************************************************************************** #* * -#* Copyright (c) 2009 Yorik van Havre * +#* Copyright (c) 2009 Yorik van Havre * #* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU General Public License (GPL) * +#* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * diff --git a/src/Mod/Draft/importSVG.py b/src/Mod/Draft/importSVG.py index 3d4b3b55b..4eb5f3752 100644 --- a/src/Mod/Draft/importSVG.py +++ b/src/Mod/Draft/importSVG.py @@ -38,7 +38,7 @@ currently unsupported: use, image # implement inherting fill style from group # handle relative units -import xml.sax, string, FreeCAD, os, math, re, Draft, DraftVecUtils, DraftGeomUtils +import xml.sax, string, FreeCAD, os, math, re, Draft, DraftVecUtils from FreeCAD import Vector try: import FreeCADGui @@ -283,6 +283,7 @@ def makewire(path,checkclosed=False,donttry=False): #ToDo Do not catch all exceptions if not donttry: try: + import DraftGeomUtils sh = Part.Wire(DraftGeomUtils.sortEdges(path)) #sh = Part.Wire(path) isok = (not checkclosed) or sh.isClosed() diff --git a/src/Mod/Drawing/App/CMakeLists.txt b/src/Mod/Drawing/App/CMakeLists.txt index 00710b75e..06d6ca7d2 100644 --- a/src/Mod/Drawing/App/CMakeLists.txt +++ b/src/Mod/Drawing/App/CMakeLists.txt @@ -65,12 +65,6 @@ if(MSVC) ADD_MSVC_PRECOMPILED_HEADER("PreCompiled.h" "PreCompiled.cpp" Drawing_CPP_SRCS) endif(MSVC) -# Set special compiler flag to convert a SIGSEV into an exception -# to handle issue #0000478. -IF(MSVC) -SET_SOURCE_FILES_PROPERTIES(ProjectionAlgos.cpp PROPERTIES COMPILE_FLAGS "/EHa") -ENDIF(MSVC) - add_library(Drawing SHARED ${Drawing_SRCS} ${Features_SRCS} ${DrawingAlgos_SRCS}) target_link_libraries(Drawing ${Drawing_LIBS}) @@ -94,6 +88,9 @@ if(MSVC) set_target_properties(Drawing PROPERTIES DEBUG_OUTPUT_NAME "Drawing_d") set_target_properties(Drawing PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Mod/Drawing) set_target_properties(Drawing PROPERTIES PREFIX "../") + # Set special compiler flag to convert a SIGSEV into an exception + # to fix issue #0000478 + set_target_properties(Drawing PROPERTIES COMPILE_FLAGS "/EHa") elseif(MINGW) set_target_properties(Drawing PROPERTIES SUFFIX ".pyd") set_target_properties(Drawing PROPERTIES DEBUG_OUTPUT_NAME "Drawing_d") diff --git a/src/Mod/Drawing/App/FeatureView.cpp b/src/Mod/Drawing/App/FeatureView.cpp index fea2121a8..e570ca46f 100644 --- a/src/Mod/Drawing/App/FeatureView.cpp +++ b/src/Mod/Drawing/App/FeatureView.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include +# include #endif @@ -63,6 +64,19 @@ FeatureView::~FeatureView() { } +App::DocumentObjectExecReturn *FeatureView::recompute(void) +{ + try { + return App::DocumentObject::recompute(); + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + App::DocumentObjectExecReturn* ret = new App::DocumentObjectExecReturn(e->GetMessageString()); + if (ret->Why.empty()) ret->Why = "Unknown OCC exception"; + return ret; + } +} + App::DocumentObjectExecReturn *FeatureView::execute(void) { return App::DocumentObject::StdReturn; diff --git a/src/Mod/Drawing/App/FeatureView.h b/src/Mod/Drawing/App/FeatureView.h index 5b3d23950..9d4607fff 100644 --- a/src/Mod/Drawing/App/FeatureView.h +++ b/src/Mod/Drawing/App/FeatureView.h @@ -55,6 +55,7 @@ public: /** @name methods overide Feature */ //@{ /// recalculate the Feature + virtual App::DocumentObjectExecReturn *recompute(void); virtual App::DocumentObjectExecReturn *execute(void); //@} diff --git a/src/Mod/Drawing/App/ProjectionAlgos.cpp b/src/Mod/Drawing/App/ProjectionAlgos.cpp index 38ca1f48d..d35412613 100644 --- a/src/Mod/Drawing/App/ProjectionAlgos.cpp +++ b/src/Mod/Drawing/App/ProjectionAlgos.cpp @@ -120,9 +120,6 @@ void ProjectionAlgos::execute(void) brep_hlr->Add(Input); try { -#if defined(__GNUC__) && defined (FC_OS_LINUX) - Base::SignalException se; -#endif gp_Ax2 transform(gp_Pnt(0,0,0),gp_Dir(Direction.x,Direction.y,Direction.z)); HLRAlgo_Projector projector( transform ); brep_hlr->Projector(projector); diff --git a/src/Mod/Fem/App/FemMeshProperty.cpp b/src/Mod/Fem/App/FemMeshProperty.cpp index 10e01fa9f..022337471 100755 --- a/src/Mod/Fem/App/FemMeshProperty.cpp +++ b/src/Mod/Fem/App/FemMeshProperty.cpp @@ -113,7 +113,7 @@ void PropertyFemMesh::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'FemMesh', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Import/Gui/AppImportGuiPy.cpp b/src/Mod/Import/Gui/AppImportGuiPy.cpp index f9ce41189..d5cef5a02 100644 --- a/src/Mod/Import/Gui/AppImportGuiPy.cpp +++ b/src/Mod/Import/Gui/AppImportGuiPy.cpp @@ -57,6 +57,7 @@ # include # include # include +# include #if OCC_VERSION_HEX >= 0x060500 # include # else @@ -74,6 +75,8 @@ #include #include #include +#include +#include @@ -507,40 +510,60 @@ static PyObject * importer(PyObject *self, PyObject *args) hApp->NewDocument(TCollection_ExtendedString("MDTV-CAF"), hDoc); if (file.hasExtension("stp") || file.hasExtension("step")) { - STEPCAFControl_Reader aReader; - aReader.SetColorMode(true); - aReader.SetNameMode(true); - aReader.SetLayerMode(true); - if (aReader.ReadFile((Standard_CString)Name) != IFSelect_RetDone) { - PyErr_SetString(PyExc_Exception, "cannot read STEP file"); - return 0; - } + try { + STEPCAFControl_Reader aReader; + aReader.SetColorMode(true); + aReader.SetNameMode(true); + aReader.SetLayerMode(true); + if (aReader.ReadFile((Standard_CString)Name) != IFSelect_RetDone) { + PyErr_SetString(PyExc_Exception, "cannot read STEP file"); + return 0; + } - Handle_Message_ProgressIndicator pi = new Part::ProgressIndicator(100); - aReader.Reader().WS()->MapReader()->SetProgress(pi); - pi->NewScope(100, "Reading STEP file..."); - pi->Show(); - aReader.Transfer(hDoc); - pi->EndScope(); + Handle_Message_ProgressIndicator pi = new Part::ProgressIndicator(100); + aReader.Reader().WS()->MapReader()->SetProgress(pi); + pi->NewScope(100, "Reading STEP file..."); + pi->Show(); + aReader.Transfer(hDoc); + pi->EndScope(); + } + catch (OSD_Exception) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + Base::Console().Error("%s\n", e->GetMessageString()); + Base::Console().Message("Try to load STEP file without colors...\n"); + + Part::ImportStepParts(pcDoc,Name); + pcDoc->recompute(); + } } else if (file.hasExtension("igs") || file.hasExtension("iges")) { - IGESControl_Controller::Init(); - Interface_Static::SetIVal("read.surfacecurve.mode",3); - IGESCAFControl_Reader aReader; - aReader.SetColorMode(true); - aReader.SetNameMode(true); - aReader.SetLayerMode(true); - if (aReader.ReadFile((Standard_CString)Name) != IFSelect_RetDone) { - PyErr_SetString(PyExc_Exception, "cannot read IGES file"); - return 0; - } + try { + IGESControl_Controller::Init(); + Interface_Static::SetIVal("read.surfacecurve.mode",3); + IGESCAFControl_Reader aReader; + aReader.SetColorMode(true); + aReader.SetNameMode(true); + aReader.SetLayerMode(true); + if (aReader.ReadFile((Standard_CString)Name) != IFSelect_RetDone) { + PyErr_SetString(PyExc_Exception, "cannot read IGES file"); + return 0; + } - Handle_Message_ProgressIndicator pi = new Part::ProgressIndicator(100); - aReader.WS()->MapReader()->SetProgress(pi); - pi->NewScope(100, "Reading IGES file..."); - pi->Show(); - aReader.Transfer(hDoc); - pi->EndScope(); + Handle_Message_ProgressIndicator pi = new Part::ProgressIndicator(100); + aReader.WS()->MapReader()->SetProgress(pi); + pi->NewScope(100, "Reading IGES file..."); + pi->Show(); + aReader.Transfer(hDoc); + pi->EndScope(); + } + catch (OSD_Exception) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + Base::Console().Error("%s\n", e->GetMessageString()); + Base::Console().Message("Try to load IGES file without colors...\n"); + + Part::ImportIgesParts(pcDoc,Name); + pcDoc->recompute(); + } } else { PyErr_SetString(PyExc_Exception, "no supported file format"); diff --git a/src/Mod/Import/Gui/CMakeLists.txt b/src/Mod/Import/Gui/CMakeLists.txt index 4d2862ec7..39193982c 100644 --- a/src/Mod/Import/Gui/CMakeLists.txt +++ b/src/Mod/Import/Gui/CMakeLists.txt @@ -50,6 +50,8 @@ if(MSVC) set_target_properties(ImportGui PROPERTIES DEBUG_OUTPUT_NAME "ImportGui_d") set_target_properties(ImportGui PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Mod/Import) set_target_properties(ImportGui PROPERTIES PREFIX "../") + # Set special compiler flag to convert a SIGSEV into an exception + set_target_properties(ImportGui PROPERTIES COMPILE_FLAGS "/EHa") elseif(MINGW) set_target_properties(ImportGui PROPERTIES SUFFIX ".pyd") set_target_properties(ImportGui PROPERTIES DEBUG_OUTPUT_NAME "ImportGui_d") diff --git a/src/Mod/Inspection/Gui/ViewProviderInspection.cpp b/src/Mod/Inspection/Gui/ViewProviderInspection.cpp index 1783b6380..b31eb4bd9 100644 --- a/src/Mod/Inspection/Gui/ViewProviderInspection.cpp +++ b/src/Mod/Inspection/Gui/ViewProviderInspection.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -388,21 +389,30 @@ public: this->deleteLater(); } + static void addFlag(Gui::View3DInventorViewer* view, const QString& text, const SoPickedPoint * point) + { + Gui::Flag* flag = new Gui::Flag; + QPalette p; + p.setColor(QPalette::Window, QColor(85,0,127)); + p.setColor(QPalette::Text, QColor(220,220,220)); + flag->setPalette(p); + flag->setText(text); + flag->setOrigin(point->getPoint()); + Gui::GLFlagWindow* flags = 0; + std::list glItems = view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId()); + if (glItems.empty()) { + flags = new Gui::GLFlagWindow(view); + view->addGraphicsItem(flags); + } + else { + flags = static_cast(glItems.front()); + } + flags->addFlag(flag, Gui::FlagLayout::BottomLeft); + } + private: QPointer widget; }; - -void addFlag(Gui::View3DInventorViewer* view, const QString& text, const SoPickedPoint * point) -{ - Gui::Flag* flag = new Gui::Flag; - QPalette p; - p.setColor(QPalette::Window, QColor(85,0,127)); - p.setColor(QPalette::Text, QColor(220,220,220)); - flag->setPalette(p); - flag->setText(text); - flag->setOrigin(point->getPoint()); - view->addFlag(flag, Gui::FlagLayout::BottomLeft); -} } void ViewProviderInspection::inspectCallback(void * ud, SoEventCallback * n) @@ -456,7 +466,7 @@ void ViewProviderInspection::inspectCallback(void * ud, SoEventCallback * n) QString info = that->inspectDistance(point); Gui::getMainWindow()->setPaneText(1,info); if (addflag) - addFlag(view, info, point); + ViewProviderProxyObject::addFlag(view, info, point); else Gui::ToolTip::showText(QCursor::pos(), info); } @@ -476,7 +486,7 @@ void ViewProviderInspection::inspectCallback(void * ud, SoEventCallback * n) QString info = that->inspectDistance(point); Gui::getMainWindow()->setPaneText(1,info); if (addflag) - addFlag(view, info, point); + ViewProviderProxyObject::addFlag(view, info, point); else Gui::ToolTip::showText(QCursor::pos(), info); break; diff --git a/src/Mod/Mesh/App/Mesh.cpp b/src/Mod/Mesh/App/Mesh.cpp index fb5084b64..11fd04fd2 100644 --- a/src/Mod/Mesh/App/Mesh.cpp +++ b/src/Mod/Mesh/App/Mesh.cpp @@ -832,23 +832,9 @@ void MeshObject::crossSections(const std::vector& planes, st } } -void MeshObject::cut(const std::vector& polygon, MeshObject::CutType type) +void MeshObject::cut(const Base::Polygon2D& polygon2d, + const Base::ViewProjMethod& proj, MeshObject::CutType type) { - MeshCore::FlatTriangulator tria; - tria.SetPolygon(polygon); - // this gives us the inverse matrix - Base::Matrix4D inv = tria.GetTransformToFitPlane(); - // compute the matrix for the coordinate transformation - Base::Matrix4D mat = inv; - mat.inverseOrthogonal(); - - std::vector poly = tria.ProjectToFitPlane(); - - Base::ViewProjMatrix proj(mat); - Base::Polygon2D polygon2d; - for (std::vector::const_iterator it = poly.begin(); it != poly.end(); ++it) - polygon2d.Add(Base::Vector2D(it->x, it->y)); - MeshCore::MeshAlgorithm meshAlg(this->_kernel); std::vector check; @@ -868,22 +854,9 @@ void MeshObject::cut(const std::vector& polygon, MeshObject::Cut this->deleteFacets(check); } -void MeshObject::trim(const std::vector& polygon, MeshObject::CutType type) +void MeshObject::trim(const Base::Polygon2D& polygon2d, + const Base::ViewProjMethod& proj, MeshObject::CutType type) { - MeshCore::FlatTriangulator tria; - tria.SetPolygon(polygon); - // this gives us the inverse matrix - Base::Matrix4D inv = tria.GetTransformToFitPlane(); - // compute the matrix for the coordinate transformation - Base::Matrix4D mat = inv; - mat.inverseOrthogonal(); - - std::vector poly = tria.ProjectToFitPlane(); - - Base::ViewProjMatrix proj(mat); - Base::Polygon2D polygon2d; - for (std::vector::const_iterator it = poly.begin(); it != poly.end(); ++it) - polygon2d.Add(Base::Vector2D(it->x, it->y)); MeshCore::MeshTrimming trim(this->_kernel, &proj, polygon2d); std::vector check; std::vector triangle; diff --git a/src/Mod/Mesh/App/Mesh.h b/src/Mod/Mesh/App/Mesh.h index a88a06ec3..18a883d2c 100644 --- a/src/Mod/Mesh/App/Mesh.h +++ b/src/Mod/Mesh/App/Mesh.h @@ -49,6 +49,11 @@ namespace Py { class List; } +namespace Base { + class Polygon2D; + class ViewProjMethod; +} + namespace MeshCore { class AbstractPolygonTriangulator; } @@ -200,8 +205,8 @@ public: Base::Vector3d getPointNormal(unsigned long) const; void crossSections(const std::vector&, std::vector §ions, float fMinEps = 1.0e-2f, bool bConnectPolygons = false) const; - void cut(const std::vector& polygon, CutType); - void trim(const std::vector& polygon, CutType); + void cut(const Base::Polygon2D& polygon, const Base::ViewProjMethod& proj, CutType); + void trim(const Base::Polygon2D& polygon, const Base::ViewProjMethod& proj, CutType); //@} /** @name Selection */ diff --git a/src/Mod/Mesh/App/MeshProperties.cpp b/src/Mod/Mesh/App/MeshProperties.cpp index 9e68cae10..552cf54bf 100644 --- a/src/Mod/Mesh/App/MeshProperties.cpp +++ b/src/Mod/Mesh/App/MeshProperties.cpp @@ -261,7 +261,7 @@ PyObject* PropertyCurvatureList::getPyObject(void) void PropertyCurvatureList::setPyObject(PyObject *value) { - throw Py::AttributeError(std::string("This attribute is read-only")); + throw Base::AttributeError(std::string("This attribute is read-only")); } App::Property *PropertyCurvatureList::Copy(void) const @@ -430,7 +430,7 @@ void PropertyMeshKernel::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Mesh', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Mesh/App/MeshPyImp.cpp b/src/Mod/Mesh/App/MeshPyImp.cpp index c25c6322b..158ef0d43 100644 --- a/src/Mod/Mesh/App/MeshPyImp.cpp +++ b/src/Mod/Mesh/App/MeshPyImp.cpp @@ -1295,7 +1295,21 @@ PyObject* MeshPy::cut(PyObject *args) polygon.push_back(Base::convertTo(pnt)); } - getMeshObjectPtr()->cut(polygon, MeshObject::CutType(mode)); + MeshCore::FlatTriangulator tria; + tria.SetPolygon(polygon); + // this gives us the inverse matrix + Base::Matrix4D inv = tria.GetTransformToFitPlane(); + // compute the matrix for the coordinate transformation + Base::Matrix4D mat = inv; + mat.inverseOrthogonal(); + + polygon = tria.ProjectToFitPlane(); + + Base::ViewProjMatrix proj(mat); + Base::Polygon2D polygon2d; + for (std::vector::const_iterator it = polygon.begin(); it != polygon.end(); ++it) + polygon2d.Add(Base::Vector2D(it->x, it->y)); + getMeshObjectPtr()->cut(polygon2d, proj, MeshObject::CutType(mode)); Py_Return; } @@ -1315,7 +1329,21 @@ PyObject* MeshPy::trim(PyObject *args) polygon.push_back(Base::convertTo(pnt)); } - getMeshObjectPtr()->trim(polygon, MeshObject::CutType(mode)); + MeshCore::FlatTriangulator tria; + tria.SetPolygon(polygon); + // this gives us the inverse matrix + Base::Matrix4D inv = tria.GetTransformToFitPlane(); + // compute the matrix for the coordinate transformation + Base::Matrix4D mat = inv; + mat.inverseOrthogonal(); + + polygon = tria.ProjectToFitPlane(); + + Base::ViewProjMatrix proj(mat); + Base::Polygon2D polygon2d; + for (std::vector::const_iterator it = polygon.begin(); it != polygon.end(); ++it) + polygon2d.Add(Base::Vector2D(it->x, it->y)); + getMeshObjectPtr()->trim(polygon2d, proj, MeshObject::CutType(mode)); Py_Return; } diff --git a/src/Mod/Mesh/Gui/Command.cpp b/src/Mod/Mesh/Gui/Command.cpp index 2e71e4ed9..4c32b1e07 100644 --- a/src/Mod/Mesh/Gui/Command.cpp +++ b/src/Mod/Mesh/Gui/Command.cpp @@ -800,6 +800,94 @@ bool CmdMeshPolyTrim::isActive(void) //-------------------------------------------------------------------------------------- +DEF_STD_CMD_A(CmdMeshTrimByPlane); + +CmdMeshTrimByPlane::CmdMeshTrimByPlane() + : Command("Mesh_TrimByPlane") +{ + sAppModule = "Mesh"; + sGroup = QT_TR_NOOP("Mesh"); + sMenuText = QT_TR_NOOP("Trim mesh with a plane"); + sToolTipText = QT_TR_NOOP("Trims a mesh with a plane"); + sStatusTip = QT_TR_NOOP("Trims a mesh with a plane"); +} + +void CmdMeshTrimByPlane::activated(int iMsg) +{ + Base::Type partType = Base::Type::fromName("Part::Plane"); + std::vector plane = getSelection().getObjectsOfType(partType); + if (plane.empty()) { + QMessageBox::warning(Gui::getMainWindow(), + qApp->translate("Mesh_TrimByPlane", "Select plane"), + qApp->translate("Mesh_TrimByPlane", "Please select a plane at which you trim the mesh.")); + return; + } + + Base::Placement plm = static_cast(plane.front())->Placement.getValue(); + Base::Vector3d normal(0,0,1); + plm.getRotation().multVec(normal, normal); + Base::Vector3d view; + if (normal == Base::Vector3d(0,0,1)) { + view.Set(0,1,0); + } + else { + Base::Vector3d dir(0,0,1); + view = normal % dir; + } + + Base::Vector3d base = plm.getPosition(); + Base::Vector3d up = normal % view; + + Base::Rotation rot(Base::Vector3d(0,0,1), view); + Base::Matrix4D mat; + rot.getValue(mat); + Base::ViewProjMatrix proj(mat); + + openCommand("Trim with plane"); + std::vector docObj = Gui::Selection().getObjectsOfType(Mesh::Feature::getClassTypeId()); + for (std::vector::iterator it = docObj.begin(); it != docObj.end(); ++it) { + Mesh::MeshObject* mesh = static_cast(*it)->Mesh.startEditing(); + Base::BoundBox3d bbox = mesh->getBoundBox(); + double len = bbox.CalcDiagonalLength(); + // project center of bbox onto plane and use this as base point + Base::Vector3d cnt = bbox.CalcCenter(); + double dist = (cnt-base)*normal; + base = cnt - normal * dist; + + Base::Vector3d p1 = base + up * len; + Base::Vector3d p2 = base - up * len; + Base::Vector3d p3 = p2 + normal * len; + Base::Vector3d p4 = p1 + normal * len; + p1 = mat * p1; + p2 = mat * p2; + p3 = mat * p3; + p4 = mat * p4; + + Base::Polygon2D polygon2d; + polygon2d.Add(Base::Vector2D(p1.x, p1.y)); + polygon2d.Add(Base::Vector2D(p2.x, p2.y)); + polygon2d.Add(Base::Vector2D(p3.x, p3.y)); + polygon2d.Add(Base::Vector2D(p4.x, p4.y)); + + Mesh::MeshObject::CutType type = Mesh::MeshObject::INNER; + mesh->trim(polygon2d, proj, type); + static_cast(*it)->Mesh.finishEditing(); + (*it)->purgeTouched(); + } + commitCommand(); +} + +bool CmdMeshTrimByPlane::isActive(void) +{ + // Check for the selected mesh feature (all Mesh types) + if (getSelection().countObjectsOfType(Mesh::Feature::getClassTypeId()) != 1) + return false; + + return true; +} + +//-------------------------------------------------------------------------------------- + DEF_STD_CMD_A(CmdMeshPolySplit); CmdMeshPolySplit::CmdMeshPolySplit() @@ -1437,6 +1525,7 @@ void CreateMeshCommands(void) rcCmdMgr.addCommand(new CmdMeshPolyCut()); rcCmdMgr.addCommand(new CmdMeshPolySplit()); rcCmdMgr.addCommand(new CmdMeshPolyTrim()); + rcCmdMgr.addCommand(new CmdMeshTrimByPlane()); rcCmdMgr.addCommand(new CmdMeshToolMesh()); rcCmdMgr.addCommand(new CmdMeshTransform()); rcCmdMgr.addCommand(new CmdMeshEvaluation()); diff --git a/src/Mod/Mesh/Gui/MeshSelection.cpp b/src/Mod/Mesh/Gui/MeshSelection.cpp index a724f96ce..f04272801 100644 --- a/src/Mod/Mesh/Gui/MeshSelection.cpp +++ b/src/Mod/Mesh/Gui/MeshSelection.cpp @@ -29,6 +29,7 @@ # include # include # include +# include #endif #include "MeshSelection.h" @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -372,7 +374,10 @@ void MeshSelection::selectGLCallback(void * ud, SoEventCallback * n) const MeshCore::MeshKernel& kernel = mesh.getKernel(); // simply get all triangles under the polygon - vp->getFacetsFromPolygon(polygon, *view, true, faces); + SoCamera* cam = view->getCamera(); + SbViewVolume vv = cam->getViewVolume(); + Gui::ViewVolumeProjection proj(vv); + vp->getFacetsFromPolygon(polygon, proj, true, faces); if (self->onlyVisibleTriangles) { const SbVec2s& sz = view->getViewportRegion().getWindowSize(); short width,height; sz.getValue(width,height); diff --git a/src/Mod/Mesh/Gui/RemoveComponents.cpp b/src/Mod/Mesh/Gui/RemoveComponents.cpp index a66e3f500..a9d869e69 100644 --- a/src/Mod/Mesh/Gui/RemoveComponents.cpp +++ b/src/Mod/Mesh/Gui/RemoveComponents.cpp @@ -23,46 +23,14 @@ #include "PreCompiled.h" -#ifndef _PreComp_ -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#ifndef _PreComp_ +# include #endif #include "RemoveComponents.h" #include "ui_RemoveComponents.h" -#include "ViewProvider.h" -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include using namespace MeshGui; @@ -186,175 +154,6 @@ void RemoveComponents::reject() // deselect all meshes meshSel.clearSelection(); } - -void RemoveComponents::paintSelection() -{ -#if 0 - SoAnnotation* hudRoot = new SoAnnotation; - hudRoot->ref(); - - SoOrthographicCamera* hudCam = new SoOrthographicCamera(); - hudCam->viewportMapping = SoCamera::LEAVE_ALONE; - // Set the position in the window. - // [0, 0] is in the center of the screen. - // - SoTranslation* hudTrans = new SoTranslation; - hudTrans->translation.setValue(-1.0f, -1.0f, 0.0f); - - QImage image(100,100,QImage::Format_ARGB32_Premultiplied); - image.fill(0x00000000); - SoSFImage sfimage; - Gui::BitmapFactory().convert(image, sfimage); - SoImage* hudImage = new SoImage(); - hudImage->image = sfimage; - - // Assemble the parts... - // - hudRoot->addChild(hudCam); - hudRoot->addChild(hudTrans); - hudRoot->addChild(hudImage); - - Gui::View3DInventorViewer* viewer = this->getViewer(); - static_cast(viewer->getSceneGraph())->addChild(hudRoot); - - QWidget* gl = viewer->getGLWidget(); - DrawingPlane pln(hudImage->image, viewer, gl); - gl->installEventFilter(&pln); - QEventLoop loop; - QObject::connect(&pln, SIGNAL(emitSelection()), &loop, SLOT(quit())); - loop.exec(); - static_cast(viewer->getSceneGraph())->removeChild(hudRoot); -#endif -} - -// --------------------------------------- - -DrawingPlane::DrawingPlane(SoSFImage& data, SoQtViewer* s, QWidget* view) - : QObject(), data(data), glView(view), soqt(s), image(view->size(), QImage::Format_ARGB32) -{ - image.fill(qRgba(255, 255, 255, 0)); - - myPenWidth = 50; - - QRgb p = qRgba(255,255,0,0); - int q = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00); - int r = qRed(q); - int g = qGreen(q); - int b = qBlue(q); - myPenColor = qRgb(r,g,b);//Qt::yellow; - myRadius = 5.0f; -} - -DrawingPlane::~DrawingPlane() -{ -} - -void DrawingPlane::changeRadius(double radius) -{ - this->myRadius = (double)radius; -} - -void DrawingPlane::mousePressEvent(QMouseEvent *event) -{ - // Calculate the given radius from mm into px - const SbViewportRegion& vp = soqt->getViewportRegion(); - float fRatio = vp.getViewportAspectRatio(); - const SbVec2s& sp = vp.getViewportSizePixels(); - float dX, dY; vp.getViewportSize().getValue(dX, dY); - SbViewVolume vv = soqt->getCamera()->getViewVolume(fRatio); - - SbVec3f p1(0,0,0); - SbVec3f p2(0,this->myRadius,0); - vv.projectToScreen(p1, p1); - vv.projectToScreen(p2, p2); - - if (fRatio > 1.0f) { - p1[0] = (p1[0] - 0.5f*dX) / fRatio + 0.5f*dX; - p2[0] = (p2[0] - 0.5f*dX) / fRatio + 0.5f*dX; - } - else if (fRatio < 1.0f) { - p1[1] = (p1[1] - 0.5f*dY) * fRatio + 0.5f*dY; - p2[1] = (p2[1] - 0.5f*dY) * fRatio + 0.5f*dY; - } - - int x1 = p1[0] * sp[0]; - int y1 = p1[1] * sp[1]; - int x2 = p2[0] * sp[0]; - int y2 = p2[1] * sp[1]; - - //myPenWidth = 2*abs(y1-y2); - - if (event->button() == Qt::LeftButton) { - lastPoint = event->pos(); - scribbling = true; - } -} - -void DrawingPlane::mouseMoveEvent(QMouseEvent *event) -{ - if ((event->buttons() & Qt::LeftButton) && scribbling) { - const QPoint& pos = event->pos(); - drawLineTo(pos); - - // filter out some points - if (selection.isEmpty()) { - selection << pos; - } - else { - const QPoint& top = selection.last(); - if (abs(top.x()-pos.x()) > 20 || - abs(top.y()-pos.y()) > 20) - selection << pos; - } - } -} - -void DrawingPlane::mouseReleaseEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton && scribbling) { - drawLineTo(event->pos()); - scribbling = false; - /*emit*/ emitSelection(); - } -} - -bool DrawingPlane::eventFilter(QObject* o, QEvent* e) -{ - if (o == glView) { - if (e->type() == QEvent::Resize) - resizeEvent(static_cast(e)); - else if (e->type() == QEvent::MouseButtonPress) - mousePressEvent(static_cast(e)); - else if (e->type() == QEvent::MouseButtonRelease) - mouseReleaseEvent(static_cast(e)); - else if (e->type() == QEvent::MouseMove) - mouseMoveEvent(static_cast(e)); - } - - return false; -} - -void DrawingPlane::resizeEvent(QResizeEvent *event) -{ - QImage img(event->size(), QImage::Format_ARGB32); - img.fill(qRgba(255, 255, 255, 0)); - image = img; -} - -void DrawingPlane::drawLineTo(const QPoint &endPoint) -{ - QPainter painter(&image); - painter.setPen(QPen(myPenColor, myPenWidth, Qt::SolidLine, Qt::RoundCap, - Qt::RoundJoin)); - painter.setOpacity(0.5); - painter.drawLine(lastPoint.x(), image.height()-lastPoint.y(), endPoint.x(), image.height()-endPoint.y()); - - QImage img = image;//QGLWidget::convertToGLFormat(image); - int nc = img.numBytes() / ( img.width() * img.height() ); - data.setValue(SbVec2s(img.width(), img.height()), nc, img.bits()); - soqt->scheduleRedraw(); - lastPoint = endPoint; -} // ------------------------------------------------- diff --git a/src/Mod/Mesh/Gui/RemoveComponents.h b/src/Mod/Mesh/Gui/RemoveComponents.h index 96f25a131..57801811a 100644 --- a/src/Mod/Mesh/Gui/RemoveComponents.h +++ b/src/Mod/Mesh/Gui/RemoveComponents.h @@ -29,55 +29,9 @@ #include #include "MeshSelection.h" -// forward declarations -class SoNode; -class SoQtViewer; -class SoSFImage; -namespace App { class DocumentObject; } -namespace Gui { class View3DInventorViewer; } -namespace Gui { class Document; } -namespace Mesh { class Feature; } - namespace MeshGui { -class ViewProviderMesh; class Ui_RemoveComponents; -class DrawingPlane : public QObject -{ - Q_OBJECT - -public: - DrawingPlane(SoSFImage&, SoQtViewer*, QWidget* view); - virtual ~DrawingPlane(); - -protected: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *event); - void resizeEvent(QResizeEvent *event); - bool eventFilter(QObject* o, QEvent* e); - -protected Q_SLOTS: - void changeRadius(double); - -Q_SIGNALS: - void emitSelection(); - -private: - void drawLineTo(const QPoint &endPoint); - - bool scribbling; - int myPenWidth; - float myRadius; - QColor myPenColor; - QPoint lastPoint; - QList selection; - QImage image; - SoSFImage& data; - SoQtViewer* soqt; - QWidget* glView; -}; - /** * Non-modal dialog to de/select components, regions, the complete or single faces * of a mesh and delete them. @@ -110,7 +64,6 @@ public Q_SLOTS: protected: void changeEvent(QEvent *e); - void paintSelection(); private: Ui_RemoveComponents* ui; diff --git a/src/Mod/Mesh/Gui/ViewProvider.cpp b/src/Mod/Mesh/Gui/ViewProvider.cpp index 3682f76e0..21c5da129 100644 --- a/src/Mod/Mesh/Gui/ViewProvider.cpp +++ b/src/Mod/Mesh/Gui/ViewProvider.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -673,10 +674,13 @@ void ViewProviderMesh::clipMeshCallback(void * ud, SoEventCallback * n) if (!views.empty()) { Gui::Application::Instance->activeDocument()->openCommand("Cut"); for (std::vector::iterator it = views.begin(); it != views.end(); ++it) { - ViewProviderMesh* that = static_cast(*it); - if (that->getEditingMode() > -1) { - that->finishEditing(); - that->cutMesh(clPoly, *view, clip_inner); + ViewProviderMesh* self = static_cast(*it); + if (self->getEditingMode() > -1) { + self->finishEditing(); + SoCamera* cam = view->getCamera(); + SbViewVolume vv = cam->getViewVolume(); + Gui::ViewVolumeProjection proj(vv); + self->cutMesh(clPoly, proj, clip_inner); } } @@ -708,10 +712,13 @@ void ViewProviderMesh::trimMeshCallback(void * ud, SoEventCallback * n) if (!views.empty()) { Gui::Application::Instance->activeDocument()->openCommand("Cut"); for (std::vector::iterator it = views.begin(); it != views.end(); ++it) { - ViewProviderMesh* that = static_cast(*it); - if (that->getEditingMode() > -1) { - that->finishEditing(); - that->trimMesh(clPoly, *view, clip_inner); + ViewProviderMesh* self = static_cast(*it); + if (self->getEditingMode() > -1) { + self->finishEditing(); + SoCamera* cam = view->getCamera(); + SbViewVolume vv = cam->getViewVolume(); + Gui::ViewVolumeProjection proj(vv); + self->trimMesh(clPoly, proj, clip_inner); } } @@ -888,15 +895,12 @@ void ViewProviderMesh::selectGLCallback(void * ud, SoEventCallback * n) } void ViewProviderMesh::getFacetsFromPolygon(const std::vector& picked, - Gui::View3DInventorViewer &Viewer, + const Base::ViewProjMethod& proj, SbBool inner, std::vector& indices) const { #if 1 bool ok = true; - SoCamera* cam = Viewer.getCamera(); - SbViewVolume vv = cam->getViewVolume(); - Gui::ViewVolumeProjection proj(vv); Base::Polygon2D polygon; for (std::vector::const_iterator it = picked.begin(); it != picked.end(); ++it) polygon.Add(Base::Vector2D((*it)[0],(*it)[1])); @@ -1114,69 +1118,27 @@ std::vector ViewProviderMesh::getVisibleFacets(const SbViewportRe } void ViewProviderMesh::cutMesh(const std::vector& picked, - Gui::View3DInventorViewer &Viewer, SbBool inner) + const Base::ViewProjMethod& proj, SbBool inner) { // Get the facet indices inside the tool mesh std::vector indices; - getFacetsFromPolygon(picked, Viewer, inner, indices); + getFacetsFromPolygon(picked, proj, inner, indices); removeFacets(indices); } void ViewProviderMesh::trimMesh(const std::vector& polygon, - Gui::View3DInventorViewer& viewer, SbBool inner) + const Base::ViewProjMethod& proj, SbBool inner) { - // get the drawing plane - SbViewVolume vol = viewer.getCamera()->getViewVolume(); - SbPlane drawPlane = vol.getPlane(viewer.getCamera()->focalDistance.getValue()); - - std::vector indices; Mesh::MeshObject* mesh = static_cast(pcObject)->Mesh.startEditing(); - MeshCore::MeshFacetGrid meshGrid(mesh->getKernel()); - MeshCore::MeshAlgorithm meshAlg(mesh->getKernel()); -#if 0 - for (std::vector::const_iterator it = polygon.begin(); it != polygon.end(); ++it) { - // the following element - std::vector::const_iterator nt = it + 1; - if (nt == polygon.end()) - break; - else if (*it == *nt) - continue; // two adjacent vertices are equal - - SbVec3f p1,p2; - SbLine l1, l2; - vol.projectPointToLine(*it, l1); - drawPlane.intersect(l1, p1); - vol.projectPointToLine(*nt, l2); - drawPlane.intersect(l2, p2); - - SbPlane plane(l1.getPosition(), l2.getPosition(), - l1.getPosition()+l1.getDirection()); - const SbVec3f& n = plane.getNormal(); - float d = plane.getDistanceFromOrigin(); - meshAlg.GetFacetsFromPlane(meshGrid, - Base::Vector3f(n[0],n[1],n[2]), d, - Base::Vector3f(p1[0],p1[1],p1[2]), - Base::Vector3f(p2[0],p2[1],p2[2]), indices); - } -#endif - - Gui::ViewVolumeProjection proj(vol); Base::Polygon2D polygon2d; for (std::vector::const_iterator it = polygon.begin(); it != polygon.end(); ++it) polygon2d.Add(Base::Vector2D((*it)[0],(*it)[1])); - MeshCore::MeshTrimming trim(mesh->getKernel(), &proj, polygon2d); - std::vector check; - std::vector triangle; - trim.SetInnerOrOuter(inner ? MeshCore::MeshTrimming::INNER : MeshCore::MeshTrimming::OUTER); - trim.CheckFacets(meshGrid, check); - trim.TrimFacets(check, triangle); - mesh->deleteFacets(check); - if (!triangle.empty()) { - mesh->getKernel().AddFacets(triangle); - } - //Remove the facets from the mesh and open a transaction object for the undo/redo stuff - //mesh->deleteFacets(indices); + + Mesh::MeshObject::CutType type = inner ? + Mesh::MeshObject::INNER : + Mesh::MeshObject::OUTER; + mesh->trim(polygon2d, proj, type); static_cast(pcObject)->Mesh.finishEditing(); pcObject->purgeTouched(); } @@ -1258,6 +1220,11 @@ void ViewProviderMesh::faceInfoCallback(void * ud, SoEventCallback * n) view->setEditing(false); view->getWidget()->setCursor(QCursor(Qt::ArrowCursor)); view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), faceInfoCallback,ud); + std::list glItems = view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId()); + for (std::list::iterator it = glItems.begin(); it != glItems.end(); ++it) { + view->removeGraphicsItem(*it); + delete *it; + } } } else if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { @@ -1276,14 +1243,24 @@ void ViewProviderMesh::faceInfoCallback(void * ud, SoEventCallback * n) return; ViewProviderMesh* that = static_cast(vp); const SoDetail* detail = point->getDetail(that->getShapeNode()); - if ( detail && detail->getTypeId() == SoFaceDetail::getClassTypeId() ) { + if (detail && detail->getTypeId() == SoFaceDetail::getClassTypeId()) { // get the boundary to the picked facet unsigned long uFacet = ((SoFaceDetail*)detail)->getFaceIndex(); that->faceInfo(uFacet); + Gui::GLFlagWindow* flags = 0; + std::list glItems = view->getGraphicsItemsOfType(Gui::GLFlagWindow::getClassTypeId()); + if (glItems.empty()) { + flags = new Gui::GLFlagWindow(view); + view->addGraphicsItem(flags); + } + else { + flags = static_cast(glItems.front()); + } + Gui::Flag* flag = new Gui::Flag; flag->setText(QObject::tr("Index: %1").arg(uFacet)); flag->setOrigin(point->getPoint()); - view->addFlag(flag, Gui::FlagLayout::TopRight); + flags->addFlag(flag, Gui::FlagLayout::TopRight); } } } diff --git a/src/Mod/Mesh/Gui/ViewProvider.h b/src/Mod/Mesh/Gui/ViewProvider.h index 29b98065a..79648b1b9 100644 --- a/src/Mod/Mesh/Gui/ViewProvider.h +++ b/src/Mod/Mesh/Gui/ViewProvider.h @@ -52,6 +52,10 @@ namespace App { class Color; } +namespace Base { + class ViewProjMethod; +} + namespace Gui { class View3DInventorViewer; class SoFCSelection; @@ -139,7 +143,7 @@ public: void clearSelection(); void deleteSelection(); void getFacetsFromPolygon(const std::vector& picked, - Gui::View3DInventorViewer &Viewer, SbBool inner, + const Base::ViewProjMethod& proj, SbBool inner, std::vector& indices) const; std::vector getFacetsOfRegion(const SbViewportRegion&, const SbViewportRegion&, SoCamera*) const; std::vector getVisibleFacetsAfterZoom(const SbBox2s&, const SbViewportRegion&, SoCamera*) const; @@ -156,8 +160,8 @@ protected: void onChanged(const App::Property* prop); virtual void showOpenEdges(bool); void setOpenEdgeColorFrom(const App::Color& col); - virtual void cutMesh(const std::vector& picked, Gui::View3DInventorViewer &Viewer, SbBool inner); - virtual void trimMesh(const std::vector& picked, Gui::View3DInventorViewer &Viewer, SbBool inner); + virtual void cutMesh(const std::vector& picked, const Base::ViewProjMethod& proj, SbBool inner); + virtual void trimMesh(const std::vector& picked, const Base::ViewProjMethod& proj, SbBool inner); virtual void splitMesh(const MeshCore::MeshKernel& toolMesh, const Base::Vector3f& normal, SbBool inner); virtual void segmentMesh(const MeshCore::MeshKernel& toolMesh, const Base::Vector3f& normal, SbBool inner); virtual void faceInfo(unsigned long facet); diff --git a/src/Mod/Mesh/Gui/Workbench.cpp b/src/Mod/Mesh/Gui/Workbench.cpp index 25b9bd656..b5005b98d 100644 --- a/src/Mod/Mesh/Gui/Workbench.cpp +++ b/src/Mod/Mesh/Gui/Workbench.cpp @@ -190,7 +190,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Mesh_FillupHoles" << "Mesh_FillInteractiveHole" << "Mesh_RemoveComponents" << "Mesh_RemoveCompByHand" << "Mesh_AddFacet" << "Mesh_Smoothing" << "Separator" << "Mesh_BuildRegularSolid" << boolean << "Separator" << "Mesh_PolySelect" << "Mesh_PolyCut" - << "Mesh_PolySplit" << "Mesh_PolySegm" << "Mesh_PolyTrim" << "Mesh_Segmentation" + << "Mesh_PolySplit" << "Mesh_PolySegm" << "Mesh_PolyTrim" << "Mesh_TrimByPlane" << "Mesh_Segmentation" << "Mesh_VertexCurvature"; return root; } diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index ac22ca8cc..76ed19154 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -13,6 +13,7 @@ #ifndef _PreComp_ # include # include +# include # include #endif @@ -92,16 +93,13 @@ void PartExport initPart() str << OCC_VERSION_MAJOR << "." << OCC_VERSION_MINOR << "." << OCC_VERSION_MAINTENANCE; App::Application::Config()["OCC_VERSION"] = str.str(); - // see Init.py -#if defined (_OCC64) -#if OCC_VERSION_HEX < 0x060503 - App::GetApplication().addImportType("STEP (*.step *.stp)","Part"); - App::GetApplication().addExportType("STEP (*.step *.stp)","Part"); -#else - App::GetApplication().addImportType("STEP with colors (*.step *.stp)","ImportGui"); - App::GetApplication().addExportType("STEP with colors (*.step *.stp)","ImportGui"); -#endif -#endif + // This is highly experimental and we should keep an eye on it + // if we have mysterious crashes + // The argument must be 'Standard_False' to avoid FPE caused by + // Python's cmath module. +//#if defined(FC_OS_LINUX) + OSD::SetSignal(Standard_False); +//#endif PyObject* partModule = Py_InitModule3("Part", Part_methods, module_part_doc); /* mod name, table ptr */ Base::Console().Log("Loading Part module... done\n"); diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index edd96dfaf..e9286a326 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -329,7 +329,6 @@ static PyObject * makeWireString(PyObject *self, PyObject *args) float height; int track = 0; - const char* text; Py_UNICODE *unichars; Py_ssize_t pysize; @@ -342,17 +341,17 @@ static PyObject * makeWireString(PyObject *self, PyObject *args) &track)) { Base::Console().Message("** makeWireString bad args.\n"); return NULL; - } + } if (PyString_Check(intext)) { PyObject *p = Base::PyAsUnicodeObject(PyString_AsString(intext)); if (!p) { Base::Console().Message("** makeWireString can't convert PyString.\n"); return NULL; - } + } pysize = PyUnicode_GetSize(p); unichars = PyUnicode_AS_UNICODE(p); - } + } else if (PyUnicode_Check(intext)) { pysize = PyUnicode_GetSize(intext); unichars = PyUnicode_AS_UNICODE(intext); @@ -376,7 +375,7 @@ static PyObject * makeWireString(PyObject *self, PyObject *args) return (CharList); } -#else +#else static PyObject * makeWireString(PyObject *self, PyObject *args) { diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index aaf1fb612..391bb6023 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -4,9 +4,9 @@ else(MSVC) add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H) endif(MSVC) -if(FREECAD_USE_FREETYPE) - add_definitions(-DFCUseFreeType) -endif(FREECAD_USE_FREETYPE) +if(FREETYPE_FOUND) + add_definitions(-DFCUseFreeType) +endif(FREETYPE_FOUND) include_directories( ${CMAKE_BINARY_DIR}/src @@ -28,12 +28,12 @@ set(Part_LIBS FreeCADApp ) -if(FREECAD_USE_FREETYPE) +if(FREETYPE_FOUND) set(Part_LIBS ${Part_LIBS} - ${FREETYPE_LIBRARY} + ${FREETYPE_LIBRARIES} ) -endif(FREECAD_USE_FREETYPE) +endif(FREETYPE_FOUND) generate_from_xml(ArcPy) generate_from_xml(ArcOfCirclePy) @@ -127,14 +127,6 @@ SET(Features_SRCS ) SOURCE_GROUP("Features" FILES ${Features_SRCS}) -# Set special compiler flag to convert a SIGSEV into an exception -# to fix issue #0000215. -IF(MSVC) -SET_SOURCE_FILES_PROPERTIES(FeatureFillet.cpp PROPERTIES COMPILE_FLAGS "/EHa") -SET_SOURCE_FILES_PROPERTIES(FeaturePartBoolean.cpp PROPERTIES COMPILE_FLAGS "/EHa") -SET_SOURCE_FILES_PROPERTIES(FeatureExtrusion.cpp PROPERTIES COMPILE_FLAGS "/EHa") -ENDIF(MSVC) - SET(Properties_SRCS PropertyTopoShape.cpp PropertyTopoShape.h @@ -270,6 +262,9 @@ if(MSVC) set_target_properties(Part PROPERTIES DEBUG_OUTPUT_NAME "Part_d") set_target_properties(Part PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Mod/Part) set_target_properties(Part PROPERTIES PREFIX "../") + # Set special compiler flag to convert a SIGSEV into an exception + # to fix issue #0000215, #0001155, ... + set_target_properties(Part PROPERTIES COMPILE_FLAGS "/EHa") elseif(MINGW) set_target_properties(Part PROPERTIES SUFFIX ".pyd") set_target_properties(Part PROPERTIES DEBUG_OUTPUT_NAME "Part_d") diff --git a/src/Mod/Part/App/FT2FC.cpp b/src/Mod/Part/App/FT2FC.cpp index cc3c7c3f3..f1986e3f7 100644 --- a/src/Mod/Part/App/FT2FC.cpp +++ b/src/Mod/Part/App/FT2FC.cpp @@ -330,11 +330,11 @@ FT_Vector getKerning(FT_Face FTFont, UNICHAR lc, UNICHAR rc) { if(error) { ErrorMsg << "FT_Get_Kerning failed: " << error; throw std::runtime_error(ErrorMsg.str()); - } + } retXY.x = ftKern.x; retXY.y = ftKern.y; return(retXY); - } +} // Make a TopoDS_Wire from a list of TopoDS_Edges TopoShapeWirePy* edgesToWire(std::vector Edges) { @@ -350,7 +350,7 @@ TopoShapeWirePy* edgesToWire(std::vector Edges) { TopoShapeWirePy* newwire = new TopoShapeWirePy(new TopoShape (occwire)); return(newwire); - } +} -#endif //#ifdef FCUseFreeType \ No newline at end of file +#endif //#ifdef FCUseFreeType diff --git a/src/Mod/Part/App/FeatureChamfer.cpp b/src/Mod/Part/App/FeatureChamfer.cpp index 3c694f012..e857000b0 100644 --- a/src/Mod/Part/App/FeatureChamfer.cpp +++ b/src/Mod/Part/App/FeatureChamfer.cpp @@ -75,7 +75,11 @@ App::DocumentObjectExecReturn *Chamfer::execute(void) TopoDS_Shape shape = mkChamfer.Shape(); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Resulting shape is null"); + ShapeHistory history = buildHistory(mkChamfer, TopAbs_FACE, shape, base->Shape.getValue()); this->Shape.setValue(shape); + PropertyShapeHistory prop; + prop.setContainer(this); + prop.setValue(history); return App::DocumentObject::StdReturn; } catch (Standard_Failure) { diff --git a/src/Mod/Part/App/FeatureFillet.cpp b/src/Mod/Part/App/FeatureFillet.cpp index 032bcde7d..550fcaf3e 100644 --- a/src/Mod/Part/App/FeatureFillet.cpp +++ b/src/Mod/Part/App/FeatureFillet.cpp @@ -74,7 +74,11 @@ App::DocumentObjectExecReturn *Fillet::execute(void) TopoDS_Shape shape = mkFillet.Shape(); if (shape.IsNull()) return new App::DocumentObjectExecReturn("Resulting shape is null"); + ShapeHistory history = buildHistory(mkFillet, TopAbs_FACE, shape, base->Shape.getValue()); this->Shape.setValue(shape); + PropertyShapeHistory prop; + prop.setContainer(this); + prop.setValue(history); return App::DocumentObject::StdReturn; } catch (Standard_Failure) { diff --git a/src/Mod/Part/App/ImportIges.h b/src/Mod/Part/App/ImportIges.h index a6d695710..c527d8be0 100644 --- a/src/Mod/Part/App/ImportIges.h +++ b/src/Mod/Part/App/ImportIges.h @@ -33,7 +33,7 @@ class Document; namespace Part { -int ImportIgesParts(App::Document *pcDoc, const char* Name); +PartExport int ImportIgesParts(App::Document *pcDoc, const char* Name); } //namespace Part diff --git a/src/Mod/Part/App/ImportStep.h b/src/Mod/Part/App/ImportStep.h index 8709e8769..af5e98ad4 100644 --- a/src/Mod/Part/App/ImportStep.h +++ b/src/Mod/Part/App/ImportStep.h @@ -39,7 +39,7 @@ namespace Part /** The part shape property */ -int ImportStepParts(App::Document *pcDoc, const char* Name); +PartExport int ImportStepParts(App::Document *pcDoc, const char* Name); diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index 57b9d9af0..dc239da90 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -24,6 +24,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include +# include +# include +# include # include # include # include @@ -32,7 +36,9 @@ # include # include # include +# include # include +# include #endif @@ -43,10 +49,14 @@ using namespace Part; PROPERTY_SOURCE(Part::RuledSurface, Part::Feature) +const char* RuledSurface::OrientationEnums[] = {"Automatic","Forward","Reversed",NULL}; + RuledSurface::RuledSurface() { ADD_PROPERTY_TYPE(Curve1,(0),"Ruled Surface",App::Prop_None,"Curve of ruled surface"); ADD_PROPERTY_TYPE(Curve2,(0),"Ruled Surface",App::Prop_None,"Curve of ruled surface"); + ADD_PROPERTY_TYPE(Orientation,((long)0),"Ruled Surface",App::Prop_None,"Orientation of ruled surface"); + Orientation.setEnums(OrientationEnums); } short RuledSurface::mustExecute() const @@ -55,6 +65,8 @@ short RuledSurface::mustExecute() const return 1; if (Curve2.isTouched()) return 1; + if (Orientation.isTouched()) + return 1; return 0; } @@ -109,6 +121,60 @@ App::DocumentObjectExecReturn *RuledSurface::execute(void) if (curve1.IsNull() || curve2.IsNull()) return new App::DocumentObjectExecReturn("Linked shapes are empty."); + + if (Orientation.getValue() == 0) { + // Automatic + Handle_Adaptor3d_HCurve a1; + Handle_Adaptor3d_HCurve a2; + if (curve1.ShapeType() == TopAbs_EDGE && curve2.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve adapt1(TopoDS::Edge(curve1)); + BRepAdaptor_Curve adapt2(TopoDS::Edge(curve2)); + a1 = new BRepAdaptor_HCurve(adapt1); + a2 = new BRepAdaptor_HCurve(adapt2); + } + else if (curve1.ShapeType() == TopAbs_WIRE && curve2.ShapeType() == TopAbs_WIRE) { + BRepAdaptor_CompCurve adapt1(TopoDS::Wire(curve1)); + BRepAdaptor_CompCurve adapt2(TopoDS::Wire(curve2)); + a1 = new BRepAdaptor_HCompCurve(adapt1); + a2 = new BRepAdaptor_HCompCurve(adapt2); + } + + if (!a1.IsNull() && !a2.IsNull()) { + // get end points of 1st curve + gp_Pnt p1 = a1->Value(a1->FirstParameter()); + gp_Pnt p2 = a1->Value(a1->LastParameter()); + if (curve1.Orientation() == TopAbs_REVERSED) { + std::swap(p1, p2); + } + + // get end points of 2nd curve + gp_Pnt p3 = a2->Value(a2->FirstParameter()); + gp_Pnt p4 = a2->Value(a2->LastParameter()); + if (curve2.Orientation() == TopAbs_REVERSED) { + std::swap(p3, p4); + } + + // Form two triangles (P1,P2,P3) and (P4,P3,P2) and check their normals. + // If the dot product is negative then it's assumed that the resulting face + // is twisted, hence the 2nd edge is reversed. + gp_Vec v1(p1, p2); + gp_Vec v2(p1, p3); + gp_Vec n1 = v1.Crossed(v2); + + gp_Vec v3(p4, p3); + gp_Vec v4(p4, p2); + gp_Vec n2 = v3.Crossed(v4); + + if (n1.Dot(n2) < 0) { + curve2.Reverse(); + } + } + } + else if (Orientation.getValue() == 2) { + // Reverse + curve2.Reverse(); + } + if (curve1.ShapeType() == TopAbs_EDGE && curve2.ShapeType() == TopAbs_EDGE) { TopoDS_Face face = BRepFill::Face(TopoDS::Edge(curve1), TopoDS::Edge(curve2)); this->Shape.setValue(face); diff --git a/src/Mod/Part/App/PartFeatures.h b/src/Mod/Part/App/PartFeatures.h index 0bf9971e1..609ac6f04 100644 --- a/src/Mod/Part/App/PartFeatures.h +++ b/src/Mod/Part/App/PartFeatures.h @@ -38,6 +38,7 @@ class RuledSurface : public Part::Feature public: RuledSurface(); + App::PropertyEnumeration Orientation; App::PropertyLinkSub Curve1; App::PropertyLinkSub Curve2; @@ -53,6 +54,9 @@ public: protected: void onChanged (const App::Property* prop); + +private: + static const char* OrientationEnums[]; }; class Loft : public Part::Feature diff --git a/src/Mod/Part/App/PropertyGeometryList.cpp b/src/Mod/Part/App/PropertyGeometryList.cpp index 947b2a91e..bf0583276 100644 --- a/src/Mod/Part/App/PropertyGeometryList.cpp +++ b/src/Mod/Part/App/PropertyGeometryList.cpp @@ -123,7 +123,7 @@ void PropertyGeometryList::setPyObject(PyObject *value) if (!PyObject_TypeCheck(item, &(GeometryPy::Type))) { std::string error = std::string("types in list must be 'Geometry', not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = static_cast(item)->getGeometryPtr(); @@ -138,7 +138,7 @@ void PropertyGeometryList::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Geometry' or list of 'Geometry', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Part/App/PropertyTopoShape.cpp b/src/Mod/Part/App/PropertyTopoShape.cpp index 718e7fee9..7fddf8b01 100644 --- a/src/Mod/Part/App/PropertyTopoShape.cpp +++ b/src/Mod/Part/App/PropertyTopoShape.cpp @@ -200,7 +200,7 @@ void PropertyPartShape::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Shape', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Part/App/TopoShapeFacePy.xml b/src/Mod/Part/App/TopoShapeFacePy.xml index 8d18c4a04..8c8bb25a4 100644 --- a/src/Mod/Part/App/TopoShapeFacePy.xml +++ b/src/Mod/Part/App/TopoShapeFacePy.xml @@ -59,6 +59,11 @@ Make a half-space solid by this face and a reference point.
+ + + Validate the face. + + Set the tolerance for the face. diff --git a/src/Mod/Part/App/TopoShapeFacePyImp.cpp b/src/Mod/Part/App/TopoShapeFacePyImp.cpp index b0460a580..6a872b162 100644 --- a/src/Mod/Part/App/TopoShapeFacePyImp.cpp +++ b/src/Mod/Part/App/TopoShapeFacePyImp.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include # include # include # include @@ -36,6 +37,7 @@ # include # include # include +# include # include # include # include @@ -49,6 +51,10 @@ # include # include # include +# include +# include +# include +# include #endif #include @@ -445,6 +451,59 @@ PyObject* TopoShapeFacePy::makeHalfSpace(PyObject *args) } } +PyObject* TopoShapeFacePy::validate(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return 0; + + try { + const TopoDS_Face& face = TopoDS::Face(getTopoShapePtr()->_Shape); + BRepCheck_Analyzer aChecker(face); + if (!aChecker.IsValid()) { + TopoDS_Wire outerwire = ShapeAnalysis::OuterWire(face); + TopTools_IndexedMapOfShape myMap; + myMap.Add(outerwire); + + TopExp_Explorer xp(face,TopAbs_WIRE); + ShapeFix_Wire fix; + fix.SetFace(face); + fix.Load(outerwire); + fix.Perform(); + BRepBuilderAPI_MakeFace mkFace(fix.WireAPIMake()); + while (xp.More()) { + if (!myMap.Contains(xp.Current())) { + fix.Load(TopoDS::Wire(xp.Current())); + fix.Perform(); + mkFace.Add(fix.WireAPIMake()); + } + xp.Next(); + } + + aChecker.Init(mkFace.Face()); + if (!aChecker.IsValid()) { + ShapeFix_Shape fix(mkFace.Face()); + fix.SetPrecision(Precision::Confusion()); + fix.SetMaxTolerance(Precision::Confusion()); + fix.SetMaxTolerance(Precision::Confusion()); + fix.Perform(); + fix.FixWireTool()->Perform(); + fix.FixFaceTool()->Perform(); + getTopoShapePtr()->_Shape = fix.Shape(); + } + else { + getTopoShapePtr()->_Shape = mkFace.Face(); + } + } + + Py_Return; + } + catch (Standard_Failure) { + Handle_Standard_Failure e = Standard_Failure::Caught(); + PyErr_SetString(PyExc_Exception, e->GetMessageString()); + return 0; + } +} + Py::Object TopoShapeFacePy::getSurface() const { const TopoDS_Face& f = TopoDS::Face(getTopoShapePtr()->_Shape); @@ -505,28 +564,49 @@ Py::Object TopoShapeFacePy::getSurface() const { Handle_Geom_Surface s = BRep_Tool::Surface(f); Handle_Geom_SurfaceOfRevolution rev = Handle_Geom_SurfaceOfRevolution::DownCast(s); + if (rev.IsNull()) { + Handle_Geom_RectangularTrimmedSurface rect = Handle_Geom_RectangularTrimmedSurface::DownCast(s); + rev = Handle_Geom_SurfaceOfRevolution::DownCast(rect->BasisSurface()); + } if (!rev.IsNull()) { GeomSurfaceOfRevolution* surf = new GeomSurfaceOfRevolution(rev); return Py::Object(new SurfaceOfRevolutionPy(surf),true); } + else { + throw Py::RuntimeError("Failed to convert to surface of revolution"); + } } case GeomAbs_SurfaceOfExtrusion: { Handle_Geom_Surface s = BRep_Tool::Surface(f); Handle_Geom_SurfaceOfLinearExtrusion ext = Handle_Geom_SurfaceOfLinearExtrusion::DownCast(s); + if (ext.IsNull()) { + Handle_Geom_RectangularTrimmedSurface rect = Handle_Geom_RectangularTrimmedSurface::DownCast(s); + ext = Handle_Geom_SurfaceOfLinearExtrusion::DownCast(rect->BasisSurface()); + } if (!ext.IsNull()) { GeomSurfaceOfExtrusion* surf = new GeomSurfaceOfExtrusion(ext); return Py::Object(new SurfaceOfExtrusionPy(surf),true); } + else { + throw Py::RuntimeError("Failed to convert to surface of extrusion"); + } } case GeomAbs_OffsetSurface: { Handle_Geom_Surface s = BRep_Tool::Surface(f); Handle_Geom_OffsetSurface off = Handle_Geom_OffsetSurface::DownCast(s); + if (off.IsNull()) { + Handle_Geom_RectangularTrimmedSurface rect = Handle_Geom_RectangularTrimmedSurface::DownCast(s); + off = Handle_Geom_OffsetSurface::DownCast(rect->BasisSurface()); + } if (!off.IsNull()) { GeomOffsetSurface* surf = new GeomOffsetSurface(off); return Py::Object(new OffsetSurfacePy(surf),true); } + else { + throw Py::RuntimeError("Failed to convert to offset surface"); + } } case GeomAbs_OtherSurface: break; diff --git a/src/Mod/Part/App/TopoShapeSolidPyImp.cpp b/src/Mod/Part/App/TopoShapeSolidPyImp.cpp index 8af8b1300..15ef23bbd 100644 --- a/src/Mod/Part/App/TopoShapeSolidPyImp.cpp +++ b/src/Mod/Part/App/TopoShapeSolidPyImp.cpp @@ -23,8 +23,12 @@ #include "PreCompiled.h" +#include #include #include +#if OCC_VERSION_HEX >= 0x060600 +#include +#endif #include #include #include @@ -178,7 +182,11 @@ Py::Object TopoShapeSolidPy::getOuterShell(void) const TopoDS_Shell shell; const TopoDS_Shape& shape = getTopoShapePtr()->_Shape; if (!shape.IsNull() && shape.ShapeType() == TopAbs_SOLID) +#if OCC_VERSION_HEX >= 0x060600 + shell = BRepClass3d::OuterShell(TopoDS::Solid(shape)); +#else shell = BRepTools::OuterShell(TopoDS::Solid(shape)); +#endif return Py::Object(new TopoShapeShellPy(new TopoShape(shell)),true); } diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 889e46fce..8a368bf64 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -25,6 +25,7 @@ #include #include "SoBrepShape.h" +#include "SoBrepFaceSet.h" #include "SoFCShapeObject.h" #include "ViewProvider.h" #include "ViewProviderExt.h" diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index 9d9d482eb..c2d457ba2 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -128,6 +128,8 @@ SET(PartGui_SRCS SoFCShapeObject.h SoBrepShape.cpp SoBrepShape.h + SoBrepFaceSet.cpp + SoBrepFaceSet.h ViewProvider.cpp ViewProvider.h ViewProviderExt.cpp diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index f6c701ab8..f53fc7445 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -30,6 +30,7 @@ # include # include # include +# include # include # include # include @@ -42,10 +43,13 @@ # include # include # include +# include +# include #endif #include "DlgFilletEdges.h" #include "ui_DlgFilletEdges.h" +#include "SoBrepShape.h" #include "../App/PartFeature.h" #include "../App/FeatureFillet.h" @@ -59,7 +63,9 @@ #include #include #include +#include #include +#include using namespace PartGui; @@ -133,14 +139,23 @@ bool FilletRadiusModel::setData (const QModelIndex & index, const QVariant & val // -------------------------------------------------------------- namespace PartGui { - class EdgeSelection : public Gui::SelectionFilterGate + class EdgeFaceSelection : public Gui::SelectionFilterGate { + bool allowEdge; App::DocumentObject*& object; public: - EdgeSelection(App::DocumentObject*& obj) - : Gui::SelectionFilterGate((Gui::SelectionFilter*)0), object(obj) + EdgeFaceSelection(App::DocumentObject*& obj) + : Gui::SelectionFilterGate((Gui::SelectionFilter*)0), allowEdge(true), object(obj) { } + void selectEdges() + { + allowEdge = true; + } + void selectFaces() + { + allowEdge = false; + } bool allow(App::Document*pDoc, App::DocumentObject*pObj, const char*sSubName) { if (pObj != this->object) @@ -148,16 +163,21 @@ namespace PartGui { if (!sSubName || sSubName[0] == '\0') return false; std::string element(sSubName); - return element.substr(0,4) == "Edge"; + if (allowEdge) + return element.substr(0,4) == "Edge"; + else + return element.substr(0,4) == "Face"; } }; class DlgFilletEdgesP { public: App::DocumentObject* object; - EdgeSelection* selection; + EdgeFaceSelection* selection; Part::FilletBase* fillet; std::vector edge_ids; + TopTools_IndexedMapOfShape all_edges; + TopTools_IndexedMapOfShape all_faces; typedef boost::signals::connection Connection; Connection connectApplicationDeletedObject; Connection connectApplicationDeletedDocument; @@ -172,7 +192,7 @@ DlgFilletEdges::DlgFilletEdges(Part::FilletBase* fillet, QWidget* parent, Qt::WF ui->setupUi(this); d->object = 0; - d->selection = new EdgeSelection(d->object); + d->selection = new EdgeFaceSelection(d->object); Gui::Selection().addSelectionGate(d->selection); d->fillet = fillet; @@ -225,30 +245,145 @@ void DlgFilletEdges::onSelectionChanged(const Gui::SelectionChanges& msg) std::string objname = d->object->getNameInDocument(); if (docname==msg.pDocName && objname==msg.pObjectName) { QString subelement = QString::fromAscii(msg.pSubName); - QAbstractItemModel* model = ui->treeView->model(); - for (int i=0; irowCount(); ++i) { - int id = model->data(model->index(i,0), Qt::UserRole).toInt(); - QString name = QString::fromAscii("Edge%1").arg(id); - if (name == subelement) { - // ok, check the selected sub-element - Qt::CheckState checkState = - (msg.Type == Gui::SelectionChanges::AddSelection - ? Qt::Checked : Qt::Unchecked); - QVariant value(static_cast(checkState)); - QModelIndex index = model->index(i,0); - model->setData(index, value, Qt::CheckStateRole); - // select the item - ui->treeView->selectionModel()->setCurrentIndex(index,QItemSelectionModel::NoUpdate); - QItemSelection selection(index, model->index(i,1)); - ui->treeView->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); - ui->treeView->update(); - break; + if (subelement.startsWith(QLatin1String("Edge"))) { + onSelectEdge(subelement, msg.Type); + } + else if (subelement.startsWith(QLatin1String("Face"))) { + d->selection->selectEdges(); + onSelectEdgesOfFace(subelement, msg.Type); + d->selection->selectFaces(); + } + } + } + + if (msg.Type != Gui::SelectionChanges::SetPreselect && + msg.Type != Gui::SelectionChanges::RmvPreselect) + QTimer::singleShot(20, this, SLOT(onHighlightEdges())); +} + +void DlgFilletEdges::onHighlightEdges() +{ + Gui::ViewProvider* view = Gui::Application::Instance->getViewProvider(d->object); + if (view) { + // deselect all faces + { + SoSearchAction searchAction; + searchAction.setType(PartGui::SoBrepFaceSet::getClassTypeId()); + searchAction.setInterest(SoSearchAction::FIRST); + searchAction.apply(view->getRoot()); + SoPath* selectionPath = searchAction.getPath(); + if (selectionPath) { + Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::None); + action.apply(selectionPath); + } + } + // deselect all points + { + SoSearchAction searchAction; + searchAction.setType(PartGui::SoBrepPointSet::getClassTypeId()); + searchAction.setInterest(SoSearchAction::FIRST); + searchAction.apply(view->getRoot()); + SoPath* selectionPath = searchAction.getPath(); + if (selectionPath) { + Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::None); + action.apply(selectionPath); + } + } + // select the edges + { + SoSearchAction searchAction; + searchAction.setType(PartGui::SoBrepEdgeSet::getClassTypeId()); + searchAction.setInterest(SoSearchAction::FIRST); + searchAction.apply(view->getRoot()); + SoPath* selectionPath = searchAction.getPath(); + if (selectionPath) { + ParameterGrp::handle hGrp = Gui::WindowParameter::getDefaultParameter()->GetGroup("View"); + SbColor selectionColor(0.1f, 0.8f, 0.1f); + unsigned long selection = (unsigned long)(selectionColor.getPackedValue()); + selection = hGrp->GetUnsigned("SelectionColor", selection); + float transparency; + selectionColor.setPackedValue((uint32_t)selection, transparency); + + // clear the selection first + Gui::SoSelectionElementAction clear(Gui::SoSelectionElementAction::None); + clear.apply(selectionPath); + + Gui::SoSelectionElementAction action(Gui::SoSelectionElementAction::Append); + action.setColor(selectionColor); + action.apply(selectionPath); + + QAbstractItemModel* model = ui->treeView->model(); + SoLineDetail detail; + action.setElement(&detail); + for (int i=0; irowCount(); ++i) { + QVariant value = model->index(i,0).data(Qt::CheckStateRole); + Qt::CheckState checkState = static_cast(value.toInt()); + + // is item checked + if (checkState & Qt::Checked) { + // the index value of the edge + int id = model->index(i,0).data(Qt::UserRole).toInt(); + detail.setLineIndex(id-1); + action.apply(selectionPath); + } } } } } } +void DlgFilletEdges::onSelectEdge(const QString& subelement, int type) +{ + Gui::SelectionChanges::MsgType msgType = Gui::SelectionChanges::MsgType(type); + QAbstractItemModel* model = ui->treeView->model(); + for (int i=0; irowCount(); ++i) { + int id = model->data(model->index(i,0), Qt::UserRole).toInt(); + QString name = QString::fromAscii("Edge%1").arg(id); + if (name == subelement) { + // ok, check the selected sub-element + Qt::CheckState checkState = + (msgType == Gui::SelectionChanges::AddSelection + ? Qt::Checked : Qt::Unchecked); + QVariant value(static_cast(checkState)); + QModelIndex index = model->index(i,0); + model->setData(index, value, Qt::CheckStateRole); + // select the item + ui->treeView->selectionModel()->setCurrentIndex(index,QItemSelectionModel::NoUpdate); + QItemSelection selection(index, model->index(i,1)); + ui->treeView->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); + ui->treeView->update(); + break; + } + } +} + +void DlgFilletEdges::onSelectEdgesOfFace(const QString& subelement, int type) +{ + bool ok; + int index = subelement.mid(4).toInt(&ok); + if (ok) { + try { + const TopoDS_Shape& face = d->all_faces.FindKey(index); + TopTools_IndexedMapOfShape mapOfEdges; + TopExp::MapShapes(face, TopAbs_EDGE, mapOfEdges); + + for(int j = 1; j <= mapOfEdges.Extent(); ++j) { + TopoDS_Edge edge = TopoDS::Edge(mapOfEdges.FindKey(j)); + int id = d->all_edges.FindIndex(edge); + QString name = QString::fromAscii("Edge%1").arg(id); + onSelectEdge(name, type); + Gui::SelectionChanges::MsgType msgType = Gui::SelectionChanges::MsgType(type); + if (msgType == Gui::SelectionChanges::AddSelection) { + Gui::Selection().addSelection(d->object->getDocument()->getName(), + d->object->getNameInDocument(), (const char*)name.toAscii()); + } + } + } + catch (Standard_Failure) { + } + } +} + void DlgFilletEdges::onDeleteObject(const App::DocumentObject& obj) { if (d->fillet == &obj) { @@ -435,6 +570,13 @@ void DlgFilletEdges::on_shapeObject_activated(int index) if (part && part->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { d->object = part; TopoDS_Shape myShape = static_cast(part)->Shape.getValue(); + + d->all_edges.Clear(); + TopExp::MapShapes(myShape, TopAbs_EDGE, d->all_edges); + + d->all_faces.Clear(); + TopExp::MapShapes(myShape, TopAbs_FACE, d->all_faces); + // build up map edge->face TopTools_IndexedDataMapOfShapeListOfShape edge2Face; TopExp::MapShapesAndAncestors(myShape, TopAbs_EDGE, TopAbs_FACE, edge2Face); @@ -481,6 +623,16 @@ void DlgFilletEdges::on_shapeObject_activated(int index) } } +void DlgFilletEdges::on_selectEdges_toggled(bool on) +{ + if (on) d->selection->selectEdges(); +} + +void DlgFilletEdges::on_selectFaces_toggled(bool on) +{ + if (on) d->selection->selectFaces(); +} + void DlgFilletEdges::on_selectAllButton_clicked() { QAbstractItemModel* model = ui->treeView->model(); @@ -631,7 +783,6 @@ bool DlgFilletEdges::accept() QByteArray to = name.toAscii(); QByteArray from = shape.toAscii(); - Gui::Command::copyVisual(to, "ShapeColor", from); Gui::Command::copyVisual(to, "LineColor", from); Gui::Command::copyVisual(to, "PointColor", from); return true; diff --git a/src/Mod/Part/Gui/DlgFilletEdges.h b/src/Mod/Part/Gui/DlgFilletEdges.h index 4c36cd9c6..0fab0dbeb 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.h +++ b/src/Mod/Part/Gui/DlgFilletEdges.h @@ -90,15 +90,20 @@ private: void onSelectionChanged(const Gui::SelectionChanges& msg); void onDeleteObject(const App::DocumentObject&); void onDeleteDocument(const App::Document&); + void onSelectEdge(const QString& subelement, int type); + void onSelectEdgesOfFace(const QString& subelement, int type); private Q_SLOTS: void on_shapeObject_activated(int); + void on_selectEdges_toggled(bool); + void on_selectFaces_toggled(bool); void on_selectAllButton_clicked(); void on_selectNoneButton_clicked(); void on_filletType_activated(int); void on_filletStartRadius_valueChanged(double); void on_filletEndRadius_valueChanged(double); void toggleCheckState(const QModelIndex&); + void onHighlightEdges(); private: std::auto_ptr ui; diff --git a/src/Mod/Part/Gui/DlgFilletEdges.ui b/src/Mod/Part/Gui/DlgFilletEdges.ui index 5d08a55d9..9f2e4606a 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.ui +++ b/src/Mod/Part/Gui/DlgFilletEdges.ui @@ -51,27 +51,7 @@ Fillet Parameter - - - - Qt::Horizontal - - - - 221 - 20 - - - - - - - - All - - - - + None @@ -85,7 +65,7 @@ - + @@ -99,10 +79,10 @@ - + - + 6 @@ -155,6 +135,43 @@ + + + + All + + + + + + + Select faces + + + + + + + Qt::Horizontal + + + + 221 + 20 + + + + + + + + Select edges + + + true + + + diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp new file mode 100644 index 000000000..7818e35ae --- /dev/null +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -0,0 +1,987 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#if 0 +#ifndef _PreComp_ +# ifdef FC_OS_WIN32 +# include +# endif +# ifdef FC_OS_MACOSX +# include +# else +# include +# endif +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include + + +#include "SoBrepFaceSet.h" + + +using namespace PartGui; + + +SO_NODE_SOURCE(SoBrepFaceSet); + +void SoBrepFaceSet::initClass() +{ + SO_NODE_INIT_CLASS(SoBrepFaceSet, SoIndexedFaceSet, "IndexedFaceSet"); +} + +SoBrepFaceSet::SoBrepFaceSet() +{ + SO_NODE_CONSTRUCTOR(SoBrepFaceSet); + SO_NODE_ADD_FIELD(partIndex, (-1)); + SO_NODE_ADD_FIELD(highlightIndex, (-1)); + SO_NODE_ADD_FIELD(selectionIndex, (-1)); + selectionIndex.setNum(0); +} + + +SoBrepFaceSet::~SoBrepFaceSet() +{ +} + + +void SoBrepFaceSet::doAction(SoAction* action) +{ + if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { + Gui::SoHighlightElementAction* hlaction = static_cast(action); + if (!hlaction->isHighlighted()) { + this->highlightIndex = -1; + return; + } + + const SoDetail* detail = hlaction->getElement(); + if (detail) { + if (detail->isOfType(SoFaceDetail::getClassTypeId())) { + int index = static_cast(detail)->getPartIndex(); + this->highlightIndex.setValue(index); + this->highlightColor = hlaction->getColor(); + } + else { + this->highlightIndex = -1; + return; + } + } + } + else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { + Gui::SoSelectionElementAction* selaction = static_cast(action); + this->selectionColor = selaction->getColor(); + if (selaction->getType() == Gui::SoSelectionElementAction::All) { + int num = this->partIndex.getNum(); + this->selectionIndex.setNum(num); + int32_t* v = this->selectionIndex.startEditing(); + for (int i=0; iselectionIndex.finishEditing(); + return; + } + else if (selaction->getType() == Gui::SoSelectionElementAction::None) { + this->selectionIndex.setNum(0); + return; + } + + const SoDetail* detail = selaction->getElement(); + if (detail) { + if (!detail->isOfType(SoFaceDetail::getClassTypeId())) { + return; + } + + int index = static_cast(detail)->getPartIndex(); + switch (selaction->getType()) { + case Gui::SoSelectionElementAction::Append: + { + int start = this->selectionIndex.getNum(); + this->selectionIndex.set1Value(start, index); + } + break; + case Gui::SoSelectionElementAction::Remove: + { + int start = this->selectionIndex.find(index); + this->selectionIndex.deleteValues(start,1); + } + break; + default: + break; + } + } + } + + inherited::doAction(action); +} + +void SoBrepFaceSet::GLRender(SoGLRenderAction *action) +{ + SoState * state = action->getState(); + + SoMaterialBundle mb(action); + Binding mbind = this->findMaterialBinding(state); + + SoTextureCoordinateBundle tb(action, TRUE, FALSE); + SbBool doTextures = tb.needCoordinates(); + + int32_t hl_idx = this->highlightIndex.getValue(); + int32_t num_selected = this->selectionIndex.getNum(); + + if (this->coordIndex.getNum() < 3) + return; + if (num_selected > 0) + renderSelection(action); + if (hl_idx >= 0) + renderHighlight(action); + + // When setting transparency shouldGLRender() handles the rendering and returns false. + // Therefore generatePrimitives() needs to be re-implemented to handle the materials + // correctly. + if (!this->shouldGLRender(action)) + return; + +#ifdef RENDER_GLARRAYS + if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) { + if (mbind == 0) { + mb.sendFirst(); // only one material -> apply it! + renderSimpleArray(); + return; + } + else if (mbind == 1) { + renderColoredArray(&mb); + return; + } + } +#endif + + Binding nbind = this->findNormalBinding(state); + + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + const int32_t * pindices; + int numparts; + SbBool normalCacheUsed; + + SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); + + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); + + mb.sendFirst(); // make sure we have the correct material + + // just in case someone forgot + if (!mindices) mindices = cindices; + if (!nindices) nindices = cindices; + pindices = this->partIndex.getValues(0); + numparts = this->partIndex.getNum(); + + renderShape(static_cast(coords), cindices, numindices, + pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); + + // Disable caching for this node + SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); + + // Workaround for #0000433 +//#if !defined(FC_OS_WIN32) + if (hl_idx >= 0) + renderHighlight(action); + if (num_selected > 0) + renderSelection(action); +//#endif +} + +void SoBrepFaceSet::GLRenderBelowPath(SoGLRenderAction * action) +{ + inherited::GLRenderBelowPath(action); +} + + // this macro actually makes the code below more readable :-) +#define DO_VERTEX(idx) \ + if (mbind == PER_VERTEX) { \ + pointDetail.setMaterialIndex(matnr); \ + vertex.setMaterialIndex(matnr++); \ + } \ + else if (mbind == PER_VERTEX_INDEXED) { \ + pointDetail.setMaterialIndex(*mindices); \ + vertex.setMaterialIndex(*mindices++); \ + } \ + if (nbind == PER_VERTEX) { \ + pointDetail.setNormalIndex(normnr); \ + currnormal = &normals[normnr++]; \ + vertex.setNormal(*currnormal); \ + } \ + else if (nbind == PER_VERTEX_INDEXED) { \ + pointDetail.setNormalIndex(*nindices); \ + currnormal = &normals[*nindices++]; \ + vertex.setNormal(*currnormal); \ + } \ + if (tb.isFunction()) { \ + vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal)); \ + if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++); \ + } \ + else if (tbind != NONE) { \ + pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx); \ + vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++)); \ + } \ + vertex.setPoint(coords->get3(idx)); \ + pointDetail.setCoordinateIndex(idx); \ + this->shapeVertex(&vertex); + +void SoBrepFaceSet::generatePrimitives(SoAction * action) +{ + //TODO +#if 0 + inherited::generatePrimitives(action); +#else + //This is highly experimental!!! + + if (this->coordIndex.getNum() < 3) return; + + SoState * state = action->getState(); + + if (this->vertexProperty.getValue()) { + state->push(); + this->vertexProperty.getValue()->doAction(action); + } + + Binding mbind = this->findMaterialBinding(state); + Binding nbind = this->findNormalBinding(state); + + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + SbBool doTextures; + SbBool sendNormals; + SbBool normalCacheUsed; + + sendNormals = TRUE; // always generate normals + + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); + + SoTextureCoordinateBundle tb(action, FALSE, FALSE); + doTextures = tb.needCoordinates(); + + if (!sendNormals) nbind = OVERALL; + else if (normalCacheUsed && nbind == PER_VERTEX) { + nbind = PER_VERTEX_INDEXED; + } + else if (normalCacheUsed && nbind == PER_FACE_INDEXED) { + nbind = PER_FACE; + } + + if (this->getNodeType() == SoNode::VRML1) { + // For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX + // on the state. + if (mbind == PER_VERTEX) { + mbind = PER_VERTEX_INDEXED; + mindices = cindices; + } + if (nbind == PER_VERTEX) { + nbind = PER_VERTEX_INDEXED; + nindices = cindices; + } + } + + Binding tbind = NONE; + if (doTextures) { + if (tb.isFunction() && !tb.needIndices()) { + tbind = NONE; + tindices = NULL; + } + // FIXME: just call inherited::areTexCoordsIndexed() instead of + // the if-check? 20020110 mortene. + else if (SoTextureCoordinateBindingElement::get(state) == + SoTextureCoordinateBindingElement::PER_VERTEX) { + tbind = PER_VERTEX; + tindices = NULL; + } + else { + tbind = PER_VERTEX_INDEXED; + if (tindices == NULL) tindices = cindices; + } + } + + if (nbind == PER_VERTEX_INDEXED && nindices == NULL) { + nindices = cindices; + } + if (mbind == PER_VERTEX_INDEXED && mindices == NULL) { + mindices = cindices; + } + + int texidx = 0; + TriangleShape mode = POLYGON; + TriangleShape newmode; + const int32_t *viptr = cindices; + const int32_t *viendptr = viptr + numindices; + const int32_t *piptr = this->partIndex.getValues(0); + int num_partindices = this->partIndex.getNum(); + const int32_t *piendptr = piptr + num_partindices; + int32_t v1, v2, v3, v4, v5 = 0, pi; // v5 init unnecessary, but kills a compiler warning. + + SoPrimitiveVertex vertex; + SoPointDetail pointDetail; + SoFaceDetail faceDetail; + + vertex.setDetail(&pointDetail); + + SbVec3f dummynormal(0,0,1); + const SbVec3f *currnormal = &dummynormal; + if (normals) currnormal = normals; + vertex.setNormal(*currnormal); + + int matnr = 0; + int normnr = 0; + int trinr = 0; + pi = piptr < piendptr ? *piptr++ : -1; + while (pi == 0) { + // It may happen that a part has no triangles + pi = piptr < piendptr ? *piptr++ : -1; + if (mbind == PER_PART) + matnr++; + else if (mbind == PER_PART_INDEXED) + mindices++; + } + + while (viptr + 2 < viendptr) { + v1 = *viptr++; + v2 = *viptr++; + v3 = *viptr++; + if (v1 < 0 || v2 < 0 || v3 < 0) { + break; + } + v4 = viptr < viendptr ? *viptr++ : -1; + if (v4 < 0) newmode = TRIANGLES; + else { + v5 = viptr < viendptr ? *viptr++ : -1; + if (v5 < 0) newmode = QUADS; + else newmode = POLYGON; + } + if (newmode != mode) { + if (mode != POLYGON) this->endShape(); + mode = newmode; + this->beginShape(action, mode, &faceDetail); + } + else if (mode == POLYGON) this->beginShape(action, POLYGON, &faceDetail); + + // vertex 1 can't use DO_VERTEX + if (mbind == PER_PART) { + if (trinr == 0) { + pointDetail.setMaterialIndex(matnr); + vertex.setMaterialIndex(matnr++); + } + } + else if (mbind == PER_PART_INDEXED) { + if (trinr == 0) { + pointDetail.setMaterialIndex(*mindices); + vertex.setMaterialIndex(*mindices++); + } + } + else if (mbind == PER_VERTEX || mbind == PER_FACE) { + pointDetail.setMaterialIndex(matnr); + vertex.setMaterialIndex(matnr++); + } + else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) { + pointDetail.setMaterialIndex(*mindices); + vertex.setMaterialIndex(*mindices++); + } + if (nbind == PER_VERTEX || nbind == PER_FACE) { + pointDetail.setNormalIndex(normnr); + currnormal = &normals[normnr++]; + vertex.setNormal(*currnormal); + } + else if (nbind == PER_FACE_INDEXED || nbind == PER_VERTEX_INDEXED) { + pointDetail.setNormalIndex(*nindices); + currnormal = &normals[*nindices++]; + vertex.setNormal(*currnormal); + } + + if (tb.isFunction()) { + vertex.setTextureCoords(tb.get(coords->get3(v1), *currnormal)); + if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++); + } + else if (tbind != NONE) { + pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx); + vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++)); + } + pointDetail.setCoordinateIndex(v1); + vertex.setPoint(coords->get3(v1)); + this->shapeVertex(&vertex); + + DO_VERTEX(v2); + DO_VERTEX(v3); + + if (mode != TRIANGLES) { + DO_VERTEX(v4); + if (mode == POLYGON) { + DO_VERTEX(v5); + v1 = viptr < viendptr ? *viptr++ : -1; + while (v1 >= 0) { + DO_VERTEX(v1); + v1 = viptr < viendptr ? *viptr++ : -1; + } + this->endShape(); + } + } + faceDetail.incFaceIndex(); + if (mbind == PER_VERTEX_INDEXED) { + mindices++; + } + if (nbind == PER_VERTEX_INDEXED) { + nindices++; + } + if (tindices) tindices++; + + trinr++; + if (pi == trinr) { + pi = piptr < piendptr ? *piptr++ : -1; + while (pi == 0) { + // It may happen that a part has no triangles + pi = piptr < piendptr ? *piptr++ : -1; + if (mbind == PER_PART) + matnr++; + else if (mbind == PER_PART_INDEXED) + mindices++; + } + trinr = 0; + } + } + if (mode != POLYGON) this->endShape(); + + if (normalCacheUsed) { + this->readUnlockNormalCache(); + } + + if (this->vertexProperty.getValue()) { + state->pop(); + } +#endif +} + +#undef DO_VERTEX + +void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action) +{ + SoState * state = action->getState(); + state->push(); + + SoLazyElement::setEmissive(state, &this->highlightColor); + SoOverrideElement::setEmissiveColorOverride(state, this, TRUE); +#if 0 // disables shading effect + // sendNormals will be FALSE + SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker); + SoOverrideElement::setDiffuseColorOverride(state, this, TRUE); + SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); +#endif + + Binding mbind = this->findMaterialBinding(state); + Binding nbind = this->findNormalBinding(state); + + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + const int32_t * pindices; + SbBool doTextures; + SbBool normalCacheUsed; + + SoMaterialBundle mb(action); + SoTextureCoordinateBundle tb(action, TRUE, FALSE); + doTextures = tb.needCoordinates(); + SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); + + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); + + mb.sendFirst(); // make sure we have the correct material + + int32_t id = this->highlightIndex.getValue(); + + // just in case someone forgot + if (!mindices) mindices = cindices; + if (!nindices) nindices = cindices; + pindices = this->partIndex.getValues(0); + + // coords + int length = (int)pindices[id]*4; + int start=0; + for (int i=0;i(coords), &(cindices[start]), length, + &(pindices[id]), 1, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); + state->pop(); +} + +void SoBrepFaceSet::renderSelection(SoGLRenderAction *action) +{ + int numSelected = this->selectionIndex.getNum(); + const int32_t* selected = this->selectionIndex.getValues(0); + if (numSelected == 0) return; + + SoState * state = action->getState(); + state->push(); + + SoLazyElement::setEmissive(state, &this->selectionColor); + SoOverrideElement::setEmissiveColorOverride(state, this, TRUE); +#if 0 // disables shading effect + SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker); + SoOverrideElement::setDiffuseColorOverride(state, this, TRUE); + SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); +#endif + + Binding mbind = this->findMaterialBinding(state); + Binding nbind = this->findNormalBinding(state); + + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + const int32_t * pindices; + SbBool doTextures; + SbBool normalCacheUsed; + + SoMaterialBundle mb(action); + SoTextureCoordinateBundle tb(action, TRUE, FALSE); + doTextures = tb.needCoordinates(); + SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); + + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); + + mb.sendFirst(); // make sure we have the correct material + + // just in case someone forgot + if (!mindices) mindices = cindices; + if (!nindices) nindices = cindices; + pindices = this->partIndex.getValues(0); + + // materials + mbind = OVERALL; + doTextures = FALSE; + + for (int i=0; i(coords), &(cindices[start]), length, + &(pindices[id]), 1, normals_s, nindices_s, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); + } + state->pop(); +} + + +SoDetail * SoBrepFaceSet::createTriangleDetail(SoRayPickAction * action, + const SoPrimitiveVertex * v1, + const SoPrimitiveVertex * v2, + const SoPrimitiveVertex * v3, + SoPickedPoint * pp) +{ + SoDetail* detail = inherited::createTriangleDetail(action, v1, v2, v3, pp); + const int32_t * indices = this->partIndex.getValues(0); + int num = this->partIndex.getNum(); + if (indices) { + SoFaceDetail* face_detail = static_cast(detail); + int index = face_detail->getFaceIndex(); + int count = 0; + for (int i=0; isetPartIndex(i); + break; + } + } + } + return detail; +} + +SoBrepFaceSet::Binding +SoBrepFaceSet::findMaterialBinding(SoState * const state) const +{ + Binding binding = OVERALL; + SoMaterialBindingElement::Binding matbind = + SoMaterialBindingElement::get(state); + + switch (matbind) { + case SoMaterialBindingElement::OVERALL: + binding = OVERALL; + break; + case SoMaterialBindingElement::PER_VERTEX: + binding = PER_VERTEX; + break; + case SoMaterialBindingElement::PER_VERTEX_INDEXED: + binding = PER_VERTEX_INDEXED; + break; + case SoMaterialBindingElement::PER_PART: + binding = PER_PART; + break; + case SoMaterialBindingElement::PER_FACE: + binding = PER_FACE; + break; + case SoMaterialBindingElement::PER_PART_INDEXED: + binding = PER_PART_INDEXED; + break; + case SoMaterialBindingElement::PER_FACE_INDEXED: + binding = PER_FACE_INDEXED; + break; + default: + break; + } + return binding; +} + +SoBrepFaceSet::Binding +SoBrepFaceSet::findNormalBinding(SoState * const state) const +{ + Binding binding = PER_VERTEX_INDEXED; + SoNormalBindingElement::Binding normbind = + (SoNormalBindingElement::Binding) SoNormalBindingElement::get(state); + + switch (normbind) { + case SoNormalBindingElement::OVERALL: + binding = OVERALL; + break; + case SoNormalBindingElement::PER_VERTEX: + binding = PER_VERTEX; + break; + case SoNormalBindingElement::PER_VERTEX_INDEXED: + binding = PER_VERTEX_INDEXED; + break; + case SoNormalBindingElement::PER_PART: + binding = PER_PART; + break; + case SoNormalBindingElement::PER_FACE: + binding = PER_FACE; + break; + case SoNormalBindingElement::PER_PART_INDEXED: + binding = PER_PART_INDEXED; + break; + case SoNormalBindingElement::PER_FACE_INDEXED: + binding = PER_FACE_INDEXED; + break; + default: + break; + } + return binding; +} + + +//**************************************************************************** +// renderShape: fallback rendering: one vertex at a time +// +void SoBrepFaceSet::renderShape(const SoGLCoordinateElement * const vertexlist, + const int32_t *vertexindices, + int num_indices, + const int32_t *partindices, + int num_partindices, + const SbVec3f *normals, + const int32_t *normalindices, + SoMaterialBundle *const materials, + const int32_t *matindices, + SoTextureCoordinateBundle * const texcoords, + const int32_t *texindices, + const int nbind, + const int mbind, + const int texture) +{ + int texidx = 0; + + const SbVec3f * coords3d = NULL; + coords3d = vertexlist->getArrayPtr3(); + + const int32_t *viptr = vertexindices; + const int32_t *viendptr = viptr + num_indices; + const int32_t *piptr = partindices; + const int32_t *piendptr = piptr + num_partindices; + int32_t v1, v2, v3, v4, pi; + SbVec3f dummynormal(0,0,1); + int numverts = vertexlist->getNum(); + + const SbVec3f *currnormal = &dummynormal; + if (normals) currnormal = normals; + + int matnr = 0; + int trinr = 0; + pi = piptr < piendptr ? *piptr++ : -1; + while (pi == 0) { + // It may happen that a part has no triangles + pi = piptr < piendptr ? *piptr++ : -1; + if (mbind == PER_PART) + matnr++; + else if (mbind == PER_PART_INDEXED) + matindices++; + } + + glBegin(GL_TRIANGLES); + while (viptr + 2 < viendptr) { + v1 = *viptr++; + v2 = *viptr++; + v3 = *viptr++; + + // This test is for robustness upon buggy data sets + if (v1 < 0 || v2 < 0 || v3 < 0 || + v1 >= numverts || v2 >= numverts || v3 >= numverts) { + break; + } + v4 = viptr < viendptr ? *viptr++ : -1; + + /* vertex 1 *********************************************************/ + if (mbind == PER_PART) { + if (trinr == 0) + materials->send(matnr++, TRUE); + } + else if (mbind == PER_PART_INDEXED) { + if (trinr == 0) + materials->send(*matindices++, TRUE); + } + else if (mbind == PER_VERTEX || mbind == PER_FACE) { + materials->send(matnr++, TRUE); + } + else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) { + materials->send(*matindices++, TRUE); + } + + if (normals) { + if (nbind == PER_VERTEX || nbind == PER_FACE) { + currnormal = normals++; + glNormal3fv((const GLfloat*)currnormal); + } + else if (nbind == PER_VERTEX_INDEXED || nbind == PER_FACE_INDEXED) { + currnormal = &normals[*normalindices++]; + glNormal3fv((const GLfloat*)currnormal); + } + } + + if (texture) { + texcoords->send(texindices ? *texindices++ : texidx++, + vertexlist->get3(v1), + *currnormal); + } + + glVertex3fv((const GLfloat*) (coords3d + v1)); + + /* vertex 2 *********************************************************/ + if (mbind == PER_VERTEX) + materials->send(matnr++, TRUE); + else if (mbind == PER_VERTEX_INDEXED) + materials->send(*matindices++, TRUE); + + if (normals) { + if (nbind == PER_VERTEX) { + currnormal = normals++; + glNormal3fv((const GLfloat*)currnormal); + } + else if (nbind == PER_VERTEX_INDEXED) { + currnormal = &normals[*normalindices++]; + glNormal3fv((const GLfloat*)currnormal); + } + } + + if (texture) { + texcoords->send(texindices ? *texindices++ : texidx++, + vertexlist->get3(v2), + *currnormal); + } + + glVertex3fv((const GLfloat*) (coords3d + v2)); + + /* vertex 3 *********************************************************/ + if (mbind == PER_VERTEX) + materials->send(matnr++, TRUE); + else if (mbind == PER_VERTEX_INDEXED) + materials->send(*matindices++, TRUE); + + if (normals) { + if (nbind == PER_VERTEX) { + currnormal = normals++; + glNormal3fv((const GLfloat*)currnormal); + } + else if (nbind == PER_VERTEX_INDEXED) { + currnormal = &normals[*normalindices++]; + glNormal3fv((const GLfloat*)currnormal); + } + } + + if (texture) { + texcoords->send(texindices ? *texindices++ : texidx++, + vertexlist->get3(v3), + *currnormal); + } + + glVertex3fv((const GLfloat*) (coords3d + v3)); + + if (mbind == PER_VERTEX_INDEXED) + matindices++; + + if (nbind == PER_VERTEX_INDEXED) + normalindices++; + + if (texture && texindices) { + texindices++; + } + + trinr++; + if (pi == trinr) { + pi = piptr < piendptr ? *piptr++ : -1; + while (pi == 0) { + // It may happen that a part has no triangles + pi = piptr < piendptr ? *piptr++ : -1; + if (mbind == PER_PART) + matnr++; + else if (mbind == PER_PART_INDEXED) + matindices++; + } + trinr = 0; + } + } + glEnd(); +} + + +#ifdef RENDER_GLARRAYS +//**************************************************************************** +// renderSimpleArray: normal and coord from vertex_array; +// no texture, color, highlight or selection but highet possible speed; +// all vertices written in one go! +// +void SoBrepFaceSet::renderSimpleArray() +{ + int cnt = index_array.size(); + + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + + glInterleavedArrays(GL_N3F_V3F, 0, vertex_array.data()); + glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT, index_array.data()); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); +} +#endif + + +#ifdef RENDER_GLARRAYS +//**************************************************************************** +// renderColoredArray: normal and coord from vertex_array; +// no texture, highlight or selection but color / material array. +// needs to iterate over parts (i.e. geometry faces) +// +void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials) +{ + int num_parts = partIndex.getNum(); + int cnt = index_array.size(); + + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + + glInterleavedArrays(GL_N3F_V3F, 0, vertex_array.data()); + const int32_t* ptr = index_array.data(); + + for (int part_id = 0; part_id < num_parts; part_id++) { + int tris = partIndex[part_id]; + + if (tris > 0) { + materials->send(part_id, TRUE); + glDrawElements(GL_TRIANGLES, 3 * tris, GL_UNSIGNED_INT, ptr); + ptr += 3 * tris; + } + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); +} +#endif + +#endif + diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.h b/src/Mod/Part/Gui/SoBrepFaceSet.h new file mode 100644 index 000000000..9259601a0 --- /dev/null +++ b/src/Mod/Part/Gui/SoBrepFaceSet.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (c) 2011 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PARTGUI_SOBREPFACESET_H +#define PARTGUI_SOBREPFACESET_H +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SoGLCoordinateElement; +class SoTextureCoordinateBundle; + + +#define RENDER_GLARRAYS + +namespace PartGui { + +class VertexArray; + + +class PartGuiExport SoBrepFaceSet : public SoIndexedFaceSet { + typedef SoIndexedFaceSet inherited; + + SO_NODE_HEADER(SoBrepFaceSet); + +public: + static void initClass(); + SoBrepFaceSet(); + + SoMFInt32 partIndex; + SoSFInt32 highlightIndex; + SoMFInt32 selectionIndex; + +#ifdef RENDER_GLARRAYS + std::vector index_array; + std::vector vertex_array; +#endif + + enum Binding { + OVERALL = 0, + PER_PART, + PER_PART_INDEXED, + PER_FACE, + PER_FACE_INDEXED, + PER_VERTEX, + PER_VERTEX_INDEXED, + NONE = OVERALL + }; + +protected: + virtual ~SoBrepFaceSet(); + virtual void GLRender(SoGLRenderAction *action); + virtual void GLRenderBelowPath(SoGLRenderAction * action); + virtual void doAction(SoAction* action); + virtual SoDetail * createTriangleDetail( + SoRayPickAction * action, + const SoPrimitiveVertex * v1, + const SoPrimitiveVertex * v2, + const SoPrimitiveVertex * v3, + SoPickedPoint * pp); + virtual void generatePrimitives(SoAction * action); + +private: + Binding findMaterialBinding(SoState * const state) const; + Binding findNormalBinding(SoState * const state) const; + void renderHighlight(SoGLRenderAction *action); + void renderSelection(SoGLRenderAction *action); + + + // low-level render functions + + void renderShape(const SoGLCoordinateElement * const vertexlist, + const int32_t *vertexindices, + int num_vertexindices, + const int32_t *partindices, + int num_partindices, + const SbVec3f *normals, + const int32_t *normindices, + SoMaterialBundle *const materials, + const int32_t *matindices, + SoTextureCoordinateBundle * const texcoords, + const int32_t *texindices, + const int nbind, + const int mbind, + const int texture); + +#ifdef RENDER_GLARRAYS + void renderSimpleArray(); + void renderColoredArray(SoMaterialBundle *const materials); +#endif + +private: + SbColor selectionColor; + SbColor highlightColor; + SoColorPacker colorpacker; +}; + +} // namespace PartGui +#endif +#endif // PARTGUI_SOBREPFACESET_H + diff --git a/src/Mod/Part/Gui/TaskFaceColors.cpp b/src/Mod/Part/Gui/TaskFaceColors.cpp index ba593b5da..60570612f 100644 --- a/src/Mod/Part/Gui/TaskFaceColors.cpp +++ b/src/Mod/Part/Gui/TaskFaceColors.cpp @@ -24,10 +24,23 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# include +# include +# include +# include +# include # include +# include # include # include # include +# include +# include +# include +# include +# include +# include +# include #endif #include @@ -36,11 +49,17 @@ #include "ui_TaskFaceColors.h" #include "TaskFaceColors.h" #include "ViewProviderExt.h" +#include "SoBrepShape.h" #include #include #include +#include #include +#include +#include +#include +#include #include #include @@ -75,6 +94,7 @@ class FaceColors::Private public: typedef boost::signals::connection Connection; Ui_TaskFaceColors* ui; + Gui::View3DInventorViewer* view; ViewProviderPartExt* vp; App::DocumentObject* obj; Gui::Document* doc; @@ -83,7 +103,7 @@ public: Connection connectDelDoc; Connection connectDelObj; - Private(ViewProviderPartExt* vp) : ui(new Ui_TaskFaceColors()), vp(vp) + Private(ViewProviderPartExt* vp) : ui(new Ui_TaskFaceColors()), view(0), vp(vp) { obj = vp->getObject(); doc = Gui::Application::Instance->getDocument(obj->getDocument()); @@ -106,6 +126,130 @@ public: { delete ui; } + bool isVisibleFace(int faceIndex, const SbVec2f& pos, Gui::View3DInventorViewer* viewer) + { + SoSeparator* root = new SoSeparator; + root->ref(); + root->addChild(viewer->getCamera()); + root->addChild(vp->getRoot()); + + SoSearchAction searchAction; + searchAction.setType(PartGui::SoBrepFaceSet::getClassTypeId()); + searchAction.setInterest(SoSearchAction::FIRST); + searchAction.apply(root); + SoPath* selectionPath = searchAction.getPath(); + + SoRayPickAction rp(viewer->getViewportRegion()); + rp.setNormalizedPoint(pos); + rp.apply(selectionPath); + root->unref(); + + SoPickedPoint* pick = rp.getPickedPoint(); + if (pick) { + const SoDetail* detail = pick->getDetail(); + if (detail && detail->isOfType(SoFaceDetail::getClassTypeId())) { + int index = static_cast(detail)->getPartIndex(); + if (faceIndex != index) + return false; + SbVec3f dir = viewer->getViewDirection(); + const SbVec3f& nor = pick->getNormal(); + if (dir.dot(nor) > 0) + return false; // bottom side points to user + return true; + } + } + + return false; + } + void addFacesToSelection(Gui::View3DInventorViewer* viewer, + const Gui::ViewVolumeProjection& proj, + const Base::Polygon2D& polygon, + const TopoDS_Shape& shape) + { + try { + TopTools_IndexedMapOfShape M; + + TopExp_Explorer xp_face(shape,TopAbs_FACE); + while (xp_face.More()) { + M.Add(xp_face.Current()); + xp_face.Next(); + } + + App::Document* appdoc = doc->getDocument(); + for (Standard_Integer k = 1; k <= M.Extent(); k++) { + const TopoDS_Shape& face = M(k); + + TopExp_Explorer xp_vertex(face,TopAbs_VERTEX); + while (xp_vertex.More()) { + gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(xp_vertex.Current())); + Base::Vector3d pt2d; + pt2d = proj(Base::Vector3d(p.X(), p.Y(), p.Z())); + if (polygon.Contains(Base::Vector2D(pt2d.x, pt2d.y))) { +#if 0 + // TODO + if (isVisibleFace(k-1, SbVec2f(pt2d.x, pt2d.y), viewer)) +#endif + { + std::stringstream str; + str << "Face" << k; + Gui::Selection().addSelection(appdoc->getName(), obj->getNameInDocument(), str.str().c_str()); + break; + } + } + xp_vertex.Next(); + } + + //GProp_GProps props; + //BRepGProp::SurfaceProperties(face, props); + //gp_Pnt c = props.CentreOfMass(); + //Base::Vector3d pt2d; + //pt2d = proj(Base::Vector3d(c.X(), c.Y(), c.Z())); + //if (polygon.Contains(Base::Vector2D(pt2d.x, pt2d.y))) { + // if (isVisibleFace(k-1, SbVec2f(pt2d.x, pt2d.y), viewer)) { + // std::stringstream str; + // str << "Face" << k; + // Gui::Selection().addSelection(appdoc->getName(), obj->getNameInDocument(), str.str().c_str()); + // } + //} + } + } + catch (...) { + } + } + static void selectionCallback(void * ud, SoEventCallback * cb) + { + Gui::View3DInventorViewer* view = reinterpret_cast(cb->getUserData()); + view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), selectionCallback, ud); + SoNode* root = view->getSceneGraph(); + static_cast(root)->selectionRole.setValue(TRUE); + + std::vector picked = view->getGLPolygon(); + SoCamera* cam = view->getCamera(); + SbViewVolume vv = cam->getViewVolume(); + Gui::ViewVolumeProjection proj(vv); + Base::Polygon2D polygon; + if (picked.size() == 2) { + SbVec2f pt1 = picked[0]; + SbVec2f pt2 = picked[1]; + polygon.Add(Base::Vector2D(pt1[0], pt1[1])); + polygon.Add(Base::Vector2D(pt1[0], pt2[1])); + polygon.Add(Base::Vector2D(pt2[0], pt2[1])); + polygon.Add(Base::Vector2D(pt2[0], pt1[1])); + } + else { + for (std::vector::const_iterator it = picked.begin(); it != picked.end(); ++it) + polygon.Add(Base::Vector2D((*it)[0],(*it)[1])); + } + + FaceColors* self = reinterpret_cast(ud); + self->d->view = 0; + if (self->d->obj && self->d->obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) { + cb->setHandled(); + const TopoDS_Shape& shape = static_cast(self->d->obj)->Shape.getValue(); + self->d->addFacesToSelection(view, proj, polygon, shape); + view->render(); + } + } }; /* TRANSLATOR PartGui::TaskFaceColors */ @@ -128,6 +272,13 @@ FaceColors::FaceColors(ViewProviderPartExt* vp, QWidget* parent) FaceColors::~FaceColors() { + if (d->view) { + d->view->stopSelection(); + d->view->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), + Private::selectionCallback, this); + SoNode* root = d->view->getSceneGraph(); + static_cast(root)->selectionRole.setValue(TRUE); + } Gui::Selection().rmvSelectionGate(); d->connectDelDoc.disconnect(); d->connectDelObj.disconnect(); @@ -146,6 +297,23 @@ void FaceColors::slotDeleteObject(const Gui::ViewProvider& obj) Gui::Control().closeDialog(); } +void FaceColors::on_boxSelection_clicked() +{ + Gui::View3DInventor* view = qobject_cast(Gui::getMainWindow()->activeWindow()); + if (view) { + Gui::View3DInventorViewer* viewer = view->getViewer(); + if (!viewer->isSelecting()) { + viewer->startSelection(Gui::View3DInventorViewer::Rubberband); + viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), Private::selectionCallback, this); + // avoid that the selection node handles the event otherwise the callback function won't be + // called immediately + SoNode* root = viewer->getSceneGraph(); + static_cast(root)->selectionRole.setValue(FALSE); + d->view = viewer; + } + } +} + void FaceColors::on_defaultButton_clicked() { std::fill(d->perface.begin(), d->perface.end(), d->vp->ShapeColor.getValue()); diff --git a/src/Mod/Part/Gui/TaskFaceColors.h b/src/Mod/Part/Gui/TaskFaceColors.h index 3ba96c9ce..be4e0e0ea 100644 --- a/src/Mod/Part/Gui/TaskFaceColors.h +++ b/src/Mod/Part/Gui/TaskFaceColors.h @@ -50,6 +50,7 @@ public: private Q_SLOTS: void on_colorButton_changed(); void on_defaultButton_clicked(); + void on_boxSelection_clicked(); protected: void onSelectionChanged(const Gui::SelectionChanges& msg); diff --git a/src/Mod/Part/Gui/TaskFaceColors.ui b/src/Mod/Part/Gui/TaskFaceColors.ui index 06da3c717..4ce48b820 100644 --- a/src/Mod/Part/Gui/TaskFaceColors.ui +++ b/src/Mod/Part/Gui/TaskFaceColors.ui @@ -14,14 +14,14 @@ Set color per face - + Click on the faces in the 3d view to select them. - + Group box @@ -61,6 +61,13 @@ + + + Box selection + + + + Qt::Vertical diff --git a/src/Mod/Part/Gui/ViewProvider.cpp b/src/Mod/Part/Gui/ViewProvider.cpp index ac9956fe5..ec2ffe269 100644 --- a/src/Mod/Part/Gui/ViewProvider.cpp +++ b/src/Mod/Part/Gui/ViewProvider.cpp @@ -73,6 +73,7 @@ # include # include # include +# include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... @@ -119,6 +120,20 @@ bool ViewProviderPart::doubleClicked(void) return true; } +void ViewProviderPart::applyColor(const Part::ShapeHistory& hist, + const std::vector& colBase, + std::vector& colBool) +{ + std::map >::const_iterator jt; + // apply color from modified faces + for (jt = hist.shapeMap.begin(); jt != hist.shapeMap.end(); ++jt) { + std::vector::const_iterator kt; + for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { + colBool[*kt] = colBase[jt->first]; + } + } +} + #else PROPERTY_SOURCE(PartGui::ViewProviderPart, PartGui::ViewProviderPartBase) diff --git a/src/Mod/Part/Gui/ViewProvider.h b/src/Mod/Part/Gui/ViewProvider.h index 50b4b8642..4d9058fd3 100644 --- a/src/Mod/Part/Gui/ViewProvider.h +++ b/src/Mod/Part/Gui/ViewProvider.h @@ -50,6 +50,8 @@ class SoScale; // Set this to use the fast rendering of shapes #define FC_USE_FAST_SHAPE_RENDERING +namespace Part { struct ShapeHistory; } + namespace PartGui { class ViewProviderShapeBuilder : public Gui::ViewProviderBuilder @@ -157,6 +159,11 @@ public: /// destructor virtual ~ViewProviderPart(); virtual bool doubleClicked(void); + +protected: + void applyColor(const Part::ShapeHistory& hist, + const std::vector& colBase, + std::vector& colBool); }; #else class PartGuiExport ViewProviderPart : public ViewProviderPartBase diff --git a/src/Mod/Part/Gui/ViewProviderBoolean.cpp b/src/Mod/Part/Gui/ViewProviderBoolean.cpp index 74aadfad5..cd7c99088 100644 --- a/src/Mod/Part/Gui/ViewProviderBoolean.cpp +++ b/src/Mod/Part/Gui/ViewProviderBoolean.cpp @@ -77,20 +77,6 @@ QIcon ViewProviderBoolean::getIcon(void) const return ViewProviderPart::getIcon(); } -void applyColor(const Part::ShapeHistory& hist, - const std::vector& colBase, - std::vector& colBool) -{ - std::map >::const_iterator jt; - // apply color from modified faces - for (jt = hist.shapeMap.begin(); jt != hist.shapeMap.end(); ++jt) { - std::vector::const_iterator kt; - for (kt = jt->second.begin(); kt != jt->second.end(); ++kt) { - colBool[*kt] = colBase[jt->first]; - } - } -} - void ViewProviderBoolean::updateData(const App::Property* prop) { PartGui::ViewProviderPart::updateData(prop); diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 7dc155b08..57eae9145 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -102,6 +102,7 @@ #include "ViewProviderExt.h" #include "SoBrepShape.h" +#include "SoBrepFaceSet.h" #include "TaskFaceColors.h" #include @@ -348,8 +349,14 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) // The correct order is Edges, Polygon offset, Faces. SoPolygonOffset* offset = new SoPolygonOffset(); + // wireframe node + SoSeparator* wireframe = new SoSeparator(); + wireframe->addChild(pcLineMaterial); + wireframe->addChild(pcLineStyle); + wireframe->addChild(lineset); + // normal viewing with edges and points - pcNormalRoot->addChild(pcWireframeRoot); + pcNormalRoot->addChild(wireframe); pcNormalRoot->addChild(offset); pcNormalRoot->addChild(pcFlatRoot); pcNormalRoot->addChild(pcPointsRoot); @@ -365,10 +372,9 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) pcFlatRoot->addChild(normb); pcFlatRoot->addChild(faceset); - // only edges - pcWireframeRoot->addChild(pcLineMaterial); - pcWireframeRoot->addChild(pcLineStyle); - pcWireframeRoot->addChild(lineset); + // edges and points + pcWireframeRoot->addChild(wireframe); + pcWireframeRoot->addChild(pcPointsRoot); // normal viewing with edges and points pcPointsRoot->addChild(pcPointMaterial); diff --git a/src/Mod/Part/Gui/ViewProviderMirror.cpp b/src/Mod/Part/Gui/ViewProviderMirror.cpp index d7c8ea5d5..9be1540ab 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.cpp +++ b/src/Mod/Part/Gui/ViewProviderMirror.cpp @@ -28,6 +28,10 @@ # include # include # include +# include +# include +# include +# include # include # include # include @@ -225,6 +229,45 @@ ViewProviderFillet::~ViewProviderFillet() { } +void ViewProviderFillet::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + if (hist.size() != 1) + return; + Part::Fillet* objFill = dynamic_cast(getObject()); + Part::Feature* objBase = dynamic_cast(objFill->Base.getValue()); + if (objBase) { + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + const TopoDS_Shape& fillShape = objFill->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap, fillMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + TopExp::MapShapes(fillShape, TopAbs_FACE, fillMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + std::vector colFill; + colFill.resize(fillMap.Extent(), static_cast(vpBase)->ShapeColor.getValue()); + + bool setColor=false; + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[0], colBase, colFill); + setColor = true; + } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[0], colBase, colFill); + setColor = true; + } + if (setColor) + this->DiffuseColor.setValues(colFill); + } + } +} + void ViewProviderFillet::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QAction* act; @@ -289,6 +332,45 @@ ViewProviderChamfer::~ViewProviderChamfer() { } +void ViewProviderChamfer::updateData(const App::Property* prop) +{ + PartGui::ViewProviderPart::updateData(prop); + if (prop->getTypeId() == Part::PropertyShapeHistory::getClassTypeId()) { + const std::vector& hist = static_cast + (prop)->getValues(); + if (hist.size() != 1) + return; + Part::Chamfer* objCham = dynamic_cast(getObject()); + Part::Feature* objBase = dynamic_cast(objCham->Base.getValue()); + if (objBase) { + const TopoDS_Shape& baseShape = objBase->Shape.getValue(); + const TopoDS_Shape& chamShape = objCham->Shape.getValue(); + + TopTools_IndexedMapOfShape baseMap, chamMap; + TopExp::MapShapes(baseShape, TopAbs_FACE, baseMap); + TopExp::MapShapes(chamShape, TopAbs_FACE, chamMap); + + Gui::ViewProvider* vpBase = Gui::Application::Instance->getViewProvider(objBase); + std::vector colBase = static_cast(vpBase)->DiffuseColor.getValues(); + std::vector colCham; + colCham.resize(chamMap.Extent(), static_cast(vpBase)->ShapeColor.getValue()); + + bool setColor=false; + if (colBase.size() == baseMap.Extent()) { + applyColor(hist[0], colBase, colCham); + setColor = true; + } + else if (!colBase.empty() && colBase[0] != this->ShapeColor.getValue()) { + colBase.resize(baseMap.Extent(), colBase[0]); + applyColor(hist[0], colBase, colCham); + setColor = true; + } + if (setColor) + this->DiffuseColor.setValues(colCham); + } + } +} + void ViewProviderChamfer::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { QAction* act; diff --git a/src/Mod/Part/Gui/ViewProviderMirror.h b/src/Mod/Part/Gui/ViewProviderMirror.h index ab0ec4c2d..802ffcc94 100644 --- a/src/Mod/Part/Gui/ViewProviderMirror.h +++ b/src/Mod/Part/Gui/ViewProviderMirror.h @@ -70,6 +70,7 @@ public: bool onDelete(const std::vector &); protected: + void updateData(const App::Property*); bool setEdit(int ModNum); void unsetEdit(int ModNum); //@} @@ -91,6 +92,7 @@ public: bool onDelete(const std::vector &); protected: + void updateData(const App::Property*); bool setEdit(int ModNum); void unsetEdit(int ModNum); //@} diff --git a/src/Mod/Part/Init.py b/src/Mod/Part/Init.py index 6da3a0373..6423a47ae 100644 --- a/src/Mod/Part/Init.py +++ b/src/Mod/Part/Init.py @@ -49,14 +49,13 @@ FreeCAD.addImportType("BREP format (*.brep *.brp)","Part") FreeCAD.addExportType("BREP format (*.brep *.brp)","Part") FreeCAD.addImportType("IGES format (*.iges *.igs)","Part") FreeCAD.addExportType("IGES format (*.iges *.igs)","Part") +FreeCAD.addImportType("STEP with colors (*.step *.stp)","ImportGui") +FreeCAD.addExportType("STEP with colors (*.step *.stp)","ImportGui") -# There is a bug in OCC 6.5.0 64-bit and older which leads to a crash -# The registration of the STEP filetype for 64-bit is handled in initPart() +# weird behaviour as 64-bit application on Windows: +# some modules like Sketcher doesn't load if Part is not loaded before with this error: +# DLL load failed: The specified procedure could not be found import platform -if platform.architecture()[0]=='32bit': - FreeCAD.addImportType("STEP with colors (*.step *.stp)","ImportGui") - FreeCAD.addExportType("STEP with colors (*.step *.stp)","ImportGui") -#else: -# FreeCAD.addImportType("STEP (*.step *.stp)","Part") -# FreeCAD.addExportType("STEP (*.step *.stp)","Part") +if platform.architecture()[0] == '64bit' and platform.system() == 'Windows': + import Part diff --git a/src/Mod/Part/MakeBottle.py b/src/Mod/Part/MakeBottle.py index 1f4c2babf..b75096bca 100644 --- a/src/Mod/Part/MakeBottle.py +++ b/src/Mod/Part/MakeBottle.py @@ -1,25 +1,6 @@ -#*************************************************************************** -#* Copyright (c) 2008 Werner Mayer * -#* * -#* This file is part of the FreeCAD CAx development system. * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU General Public License (GPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* FreeCAD is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with FreeCAD; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#*************************************************************************** +#! python +# -*- coding: utf-8 -*- +# (c) 2008 Werner Mayer LGPL import Part, math diff --git a/src/Mod/PartDesign/App/FeatureFace.cpp b/src/Mod/PartDesign/App/FeatureFace.cpp index 55030f6c9..fb4b81dcf 100644 --- a/src/Mod/PartDesign/App/FeatureFace.cpp +++ b/src/Mod/PartDesign/App/FeatureFace.cpp @@ -105,23 +105,25 @@ App::DocumentObjectExecReturn *Face::execute(void) return App::DocumentObject::StdReturn; } -namespace PartDesign { - // sort bounding boxes according to diagonal length -struct Wire_Compare { +class Face::Wire_Compare { +public: bool operator() (const TopoDS_Wire& w1, const TopoDS_Wire& w2) { Bnd_Box box1, box2; - BRepBndLib::Add(w1, box1); - box1.SetGap(0.0); + if (!w1.IsNull()) { + BRepBndLib::Add(w1, box1); + box1.SetGap(0.0); + } - BRepBndLib::Add(w2, box2); - box2.SetGap(0.0); + if (!w2.IsNull()) { + BRepBndLib::Add(w2, box2); + box2.SetGap(0.0); + } return box1.SquareExtent() < box2.SquareExtent(); } }; -} TopoDS_Shape Face::makeFace(std::list& wires) const { diff --git a/src/Mod/PartDesign/App/FeatureFace.h b/src/Mod/PartDesign/App/FeatureFace.h index b30ec92ef..2cb69bd91 100644 --- a/src/Mod/PartDesign/App/FeatureFace.h +++ b/src/Mod/PartDesign/App/FeatureFace.h @@ -48,6 +48,9 @@ public: protected: TopoDS_Shape makeFace(const std::vector&) const; TopoDS_Shape makeFace(std::list&) const; // for internal use only + +private: + class Wire_Compare; }; } //namespace PartDesign diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.cpp b/src/Mod/PartDesign/App/FeatureSketchBased.cpp index c5c856761..0e2027160 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.cpp +++ b/src/Mod/PartDesign/App/FeatureSketchBased.cpp @@ -69,18 +69,21 @@ using namespace PartDesign; -namespace PartDesign { - // sort bounding boxes according to diagonal length -struct Wire_Compare { +class SketchBased::Wire_Compare { +public: bool operator() (const TopoDS_Wire& w1, const TopoDS_Wire& w2) { Bnd_Box box1, box2; - BRepBndLib::Add(w1, box1); - box1.SetGap(0.0); + if (!w1.IsNull()) { + BRepBndLib::Add(w1, box1); + box1.SetGap(0.0); + } - BRepBndLib::Add(w2, box2); - box2.SetGap(0.0); + if (!w2.IsNull()) { + BRepBndLib::Add(w2, box2); + box2.SetGap(0.0); + } return box1.SquareExtent() < box2.SquareExtent(); } @@ -799,6 +802,7 @@ void SketchBased::remapSupportShape(const TopoDS_Shape& newShape) } } +namespace PartDesign { struct gp_Pnt_Less : public std::binary_function { @@ -814,6 +818,7 @@ struct gp_Pnt_Less : public std::binary_function * -* * -* This file is part of the FreeCAD CAx development system. * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License (GPL) * -* as published by the Free Software Foundation; either version 2 of * -* the License, or (at your option) any later version. * -* for detail see the LICENCE text file. * -* * -* FreeCAD is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public * -* License along with FreeCAD; if not, write to the Free Software * -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -* USA * -* * -*************************************************************************** """ __author__ = "Werner Mayer " diff --git a/src/Mod/PartDesign/Scripts/FilletArc.py b/src/Mod/PartDesign/Scripts/FilletArc.py index f536aa5a3..65a2aeaba 100644 --- a/src/Mod/PartDesign/Scripts/FilletArc.py +++ b/src/Mod/PartDesign/Scripts/FilletArc.py @@ -1,27 +1,6 @@ -""" -*************************************************************************** -* Copyright (c) 2010 Werner Mayer * -* * -* This file is part of the FreeCAD CAx development system. * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License (GPL) * -* as published by the Free Software Foundation; either version 2 of * -* the License, or (at your option) any later version. * -* for detail see the LICENCE text file. * -* * -* FreeCAD is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public * -* License along with FreeCAD; if not, write to the Free Software * -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -* USA * -* * -*************************************************************************** -""" +#! python +# -*- coding: utf-8 -*- +# (c) 2010 Werner Mayer LGPL __author__ = "Werner Mayer " diff --git a/src/Mod/PartDesign/Scripts/Parallelepiped.py b/src/Mod/PartDesign/Scripts/Parallelepiped.py index 98b41a7c3..41c47d3b6 100644 --- a/src/Mod/PartDesign/Scripts/Parallelepiped.py +++ b/src/Mod/PartDesign/Scripts/Parallelepiped.py @@ -1,28 +1,9 @@ +#! python +# -*- coding: utf-8 -*- +# (c) 2011 Werner Mayer LGPL + """ An example for a high-level cutsom feature object to form a full-parametric parallelepiped. - -*************************************************************************** -* Copyright (c) 2011 Werner Mayer * -* * -* This file is part of the FreeCAD CAx development system. * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License (GPL) * -* as published by the Free Software Foundation; either version 2 of * -* the License, or (at your option) any later version. * -* for detail see the LICENCE text file. * -* * -* FreeCAD is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU Library General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public * -* License along with FreeCAD; if not, write to the Free Software * -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -* USA * -* * -*************************************************************************** """ __author__ = "Werner Mayer " diff --git a/src/Mod/Points/App/PropertyPointKernel.cpp b/src/Mod/Points/App/PropertyPointKernel.cpp index f0dcf9498..9134efd8d 100644 --- a/src/Mod/Points/App/PropertyPointKernel.cpp +++ b/src/Mod/Points/App/PropertyPointKernel.cpp @@ -99,7 +99,7 @@ void PropertyPointKernel::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Points', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Robot/App/PropertyTrajectory.cpp b/src/Mod/Robot/App/PropertyTrajectory.cpp index 4b449c462..8116f24fe 100644 --- a/src/Mod/Robot/App/PropertyTrajectory.cpp +++ b/src/Mod/Robot/App/PropertyTrajectory.cpp @@ -106,7 +106,7 @@ void PropertyTrajectory::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Trajectory', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Sandbox/App/DocumentProtector.cpp b/src/Mod/Sandbox/App/DocumentProtector.cpp index a9da9bddc..36cf6042b 100644 --- a/src/Mod/Sandbox/App/DocumentProtector.cpp +++ b/src/Mod/Sandbox/App/DocumentProtector.cpp @@ -160,6 +160,25 @@ protected: const AbstractCallable& callable; }; +class CustomPurgeEvent : public AbstractCustomProtectorEvent +{ +public: + CustomPurgeEvent(App::DocumentObject* o) + : obj(o) + { + } + ~CustomPurgeEvent() + { + } + void execute() + { + obj->purgeTouched(); + } + +protected: + App::DocumentObject* obj; +}; + class DocumentReceiver : public QObject { public: @@ -324,3 +343,8 @@ void DocumentObjectProtector::execute(const AbstractCallable& call) DocumentReceiver::globalInstance()->postEventAndWait(new CustomCallableEvent(call)); } +void DocumentObjectProtector::purgeTouched() +{ + validate(); + DocumentReceiver::globalInstance()->postEventAndWait(new CustomPurgeEvent(obj)); +} diff --git a/src/Mod/Sandbox/App/DocumentProtector.h b/src/Mod/Sandbox/App/DocumentProtector.h index 6502c2642..c3ad49125 100644 --- a/src/Mod/Sandbox/App/DocumentProtector.h +++ b/src/Mod/Sandbox/App/DocumentProtector.h @@ -127,6 +127,7 @@ public: App::DocumentObject* getObject() const; bool setProperty(const std::string& name, const App::Property& p); void execute(const AbstractCallable&); + void purgeTouched(); private: void validate(); diff --git a/src/Mod/Sandbox/App/DocumentProtectorPy.cpp b/src/Mod/Sandbox/App/DocumentProtectorPy.cpp index 86f16ac06..319849fb3 100644 --- a/src/Mod/Sandbox/App/DocumentProtectorPy.cpp +++ b/src/Mod/Sandbox/App/DocumentProtectorPy.cpp @@ -118,6 +118,7 @@ int DocumentProtectorPy::setattr(const char * attr, const Py::Object & value) throw Py::RuntimeError(s_out.str()); } else { + Base::PyGILStateRelease unlock; return Py::PythonExtension::setattr(attr, value); } } @@ -161,6 +162,8 @@ void DocumentObjectProtectorPy::init_type() behaviors().supportRepr(); behaviors().supportGetattr(); behaviors().supportSetattr(); + + add_varargs_method("purgeTouched",&DocumentObjectProtectorPy::purgeTouched,"purgeTouched()"); } DocumentObjectProtectorPy::DocumentObjectProtectorPy(App::DocumentObject *obj) @@ -178,6 +181,13 @@ DocumentObjectProtectorPy::~DocumentObjectProtectorPy() delete _dp; } +Py::Object DocumentObjectProtectorPy::getObject() const +{ + App::DocumentObject* obj = _dp->getObject(); + PyObject* py = obj->getPyObject(); + return Py::Object(py, true); +} + Py::Object DocumentObjectProtectorPy::repr() { std::string s; @@ -200,10 +210,11 @@ Py::Object DocumentObjectProtectorPy::getattr(const char * attr) App::DocumentObject* obj = _dp->getObject(); App::Property* prop = obj->getPropertyByName(attr); if (!prop) { - std::string s; - std::ostringstream s_out; - s_out << "No such attribute '" << attr << "'"; - throw Py::AttributeError(s_out.str()); + return Py::PythonExtension::getattr(attr); + //std::string s; + //std::ostringstream s_out; + //s_out << "No such attribute '" << attr << "'"; + //throw Py::AttributeError(s_out.str()); } return Py::asObject(prop->getPyObject()); @@ -227,9 +238,21 @@ int DocumentObjectProtectorPy::setattr(const char * attr, const Py::Object & val s_out << "No such attribute '" << attr << "'"; throw Py::AttributeError(s_out.str()); } + Base::PyGILStateRelease unlock; std::auto_ptr copy(static_cast (prop->getTypeId().createInstance())); - copy->setPyObject(value.ptr()); + if (PyObject_TypeCheck(value.ptr(), DocumentObjectProtectorPy::type_object())) { + copy->setPyObject(static_cast(value.ptr())->getObject().ptr()); + } + else { + copy->setPyObject(value.ptr()); + } return _dp->setProperty(attr, *copy) ? 0 : -1; } } + +Py::Object DocumentObjectProtectorPy::purgeTouched(const Py::Tuple&) +{ + _dp->purgeTouched(); + return Py::None(); +} diff --git a/src/Mod/Sandbox/App/DocumentProtectorPy.h b/src/Mod/Sandbox/App/DocumentProtectorPy.h index 318aac529..368057e53 100644 --- a/src/Mod/Sandbox/App/DocumentProtectorPy.h +++ b/src/Mod/Sandbox/App/DocumentProtectorPy.h @@ -72,7 +72,9 @@ public: Py::Object repr(); Py::Object getattr(const char *); + Py::Object getObject() const; int setattr(const char *, const Py::Object &); + Py::Object purgeTouched(const Py::Tuple&); private: DocumentObjectProtector* _dp; diff --git a/src/Mod/Sandbox/Gui/Command.cpp b/src/Mod/Sandbox/Gui/Command.cpp index ea224c528..1a7e9313d 100644 --- a/src/Mod/Sandbox/Gui/Command.cpp +++ b/src/Mod/Sandbox/Gui/Command.cpp @@ -23,6 +23,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ +# ifdef FC_OS_WIN32 +# include +# endif # include # include # include diff --git a/src/Mod/Sandbox/Gui/Workbench.cpp b/src/Mod/Sandbox/Gui/Workbench.cpp index c87385295..de5753616 100644 --- a/src/Mod/Sandbox/Gui/Workbench.cpp +++ b/src/Mod/Sandbox/Gui/Workbench.cpp @@ -36,6 +36,10 @@ #include "Workbench.h" #include #include +#include +#include +#include +#include using namespace SandboxGui; @@ -44,6 +48,24 @@ TYPESYSTEM_SOURCE(SandboxGui::Workbench, Gui::StdWorkbench) Workbench::Workbench() { + // Tree view + Gui::DockWindow* tree = new Gui::DockWindow(0, Gui::getMainWindow()); + tree->setWindowTitle(QString::fromAscii("Tree view")); + Gui::TreeView* treeWidget = new Gui::TreeView(tree); + treeWidget->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + treeWidget->setIndentation(hGrp->GetInt("Indentation", treeWidget->indentation())); + + QGridLayout* pLayout = new QGridLayout(tree); + pLayout->setSpacing(0); + pLayout->setMargin (0); + pLayout->addWidget(treeWidget, 0, 0); + + tree->setObjectName + (QString::fromAscii(QT_TRANSLATE_NOOP("QDockWidget","Tree view (MVC)"))); + tree->setMinimumWidth(210); + Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); + pDockMgr->registerDockWindow("Std_TreeViewMVC", tree); } Workbench::~Workbench() @@ -109,6 +131,14 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const return 0; } +Gui::DockWindowItems* Workbench::setupDockWindows() const +{ + Gui::DockWindowItems* root = Gui::StdWorkbench::setupDockWindows(); + root->setVisibility(false); // hide all dock windows by default + root->addDockWidget("Std_TreeViewMVC", Qt::RightDockWidgetArea, true, true); + return root; +} + // ---------------------------------------------------- diff --git a/src/Mod/Sandbox/Gui/Workbench.h b/src/Mod/Sandbox/Gui/Workbench.h index f923c433a..cdc20d370 100644 --- a/src/Mod/Sandbox/Gui/Workbench.h +++ b/src/Mod/Sandbox/Gui/Workbench.h @@ -43,6 +43,7 @@ protected: Gui::MenuItem* setupMenuBar() const; Gui::ToolBarItem* setupToolBars() const; Gui::ToolBarItem* setupCommandBars() const; + Gui::DockWindowItems* setupDockWindows() const; }; class SoWidgetShape : public SoShape { diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index 8137d225d..aa87360bd 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -21,6 +21,14 @@ SET(ShipExamples_SRCS ) SOURCE_GROUP("shipexamples" FILES ${ShipExamples_SRCS}) +SET(ShipOpenCL_SRCS + resources/opencl/matrixGen.cl + resources/opencl/jacobi.cl + resources/opencl/minres.cl + resources/opencl/lsqr.cl +) +SOURCE_GROUP("shipopencl" FILES ${ShipOpenCL_SRCS}) + SET(ShipLoadExample_SRCS shipLoadExample/__init__.py shipLoadExample/TaskPanel.py @@ -107,12 +115,18 @@ SET(SimRun_SRCS simRun/TaskPanel.ui simRun/clSim/__init__.py simRun/clSim/initialization.py - simRun/clSim/Utils.py + simRun/clSim/matrixGen.py + simRun/clSim/BEMsolver.py + simRun/clSim/evolution.py + simRun/clSim/clUtils.py + simRun/clSim/bem_jacobi_cl.py + simRun/clSim/bem_minres_cl.py + simRun/clSim/bem_lsqr_cl.py simRun/Sim/__init__.py simRun/Sim/initialization.py simRun/Sim/matrixGen.py - simRun/Sim/computeSources.py - simRun/Sim/fsEvolution.py + simRun/Sim/BEMsolver.py + simRun/Sim/evolution.py ) SOURCE_GROUP("simrun" FILES ${SimRun_SRCS}) @@ -123,7 +137,7 @@ SET(SimPost_SRCS ) SOURCE_GROUP("simpost" FILES ${SimPost_SRCS}) -SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS} ${ShipGZ_SRCS} ${SimCreate_SRCS} ${SimRun_SRCS} ${SimPost_SRCS}) +SET(all_files ${ShipMain_SRCS} ${ShipIcons_SRCS} ${ShipExamples_SRCS} ${ShipOpenCL_SRCS} ${ShipLoadExample_SRCS} ${ShipCreateShip_SRCS} ${ShipOutlineDraw_SRCS} ${ShipAreasCurve_SRCS} ${ShipHydrostatics_SRCS} ${ShipUtils_SRCS} ${ShipWeights_SRCS} ${ShipCreateTank_SRCS} ${ShipGZ_SRCS} ${SimCreate_SRCS} ${SimRun_SRCS} ${SimPost_SRCS}) ADD_CUSTOM_TARGET(Ship ALL SOURCES ${all_files} @@ -143,6 +157,12 @@ INSTALL( DESTINATION Mod/Ship/resources/examples ) +INSTALL( + FILES + ${ShipOpenCL_SRCS} + DESTINATION + Mod/Ship/resources/opencl +) INSTALL( FILES ${ShipLoadExample_SRCS} diff --git a/src/Mod/Ship/InitGui.py b/src/Mod/Ship/InitGui.py index 17aa80443..8fdf24c6c 100644 --- a/src/Mod/Ship/InitGui.py +++ b/src/Mod/Ship/InitGui.py @@ -51,6 +51,7 @@ class ShipWorkbench ( Workbench ): # Simulation stuff only if pyOpenCL & numpy are present hasOpenCL = True hasNumpy = True + hasSim = False # In development, activate it only for development purposes try: import pyopencl except ImportError: @@ -65,7 +66,7 @@ class ShipWorkbench ( Workbench ): msg = QtGui.QApplication.translate("ship_console", "numpy not installed, simulations stuff will disabled therefore", None,QtGui.QApplication.UnicodeUTF8) FreeCAD.Console.PrintMessage(msg + '\n') - if hasOpenCL and hasNumpy: + if hasOpenCL and hasNumpy and hasSim: simlist = ["Ship_CreateSim", "Ship_RunSim", "Ship_StopSim", "Ship_TrackSim"] self.appendToolbar(str(QtCore.QT_TRANSLATE_NOOP("Ship", "Simulation")),simlist) self.appendMenu(str(QtCore.QT_TRANSLATE_NOOP("Ship", "Simulation")),simlist) diff --git a/src/Mod/Ship/Makefile.am b/src/Mod/Ship/Makefile.am index fd24beac6..c71bc97b5 100644 --- a/src/Mod/Ship/Makefile.am +++ b/src/Mod/Ship/Makefile.am @@ -15,6 +15,10 @@ nobase_data_DATA = \ resources/examples/wigley.fcstd \ resources/examples/wigley_katamaran.fcstd \ resources/icons/Ico.xpm \ + resources/opencl/matrixGen.cl \ + resources/opencl/jacobi.cl \ + resources/opencl/minres.cl \ + resources/opencl/lsqr.cl \ shipLoadExample/__init__.py \ shipLoadExample/TaskPanel.py \ shipLoadExample/TaskPanel.ui \ @@ -60,12 +64,18 @@ nobase_data_DATA = \ simRun/TaskPanel.ui \ simRun/clSim/__init__.py \ simRun/clSim/initialization.py \ - simRun/clSim/Utils.py \ + simRun/clSim/matrixGen.py \ + simRun/clSim/BEMsolver.py \ + simRun/clSim/evolution.py \ + simRun/clSim/clUtils.py \ + simRun/clSim/bem_jacobi_cl.py \ + simRun/clSim/bem_minres_cl.py \ + simRun/clSim/bem_lsqr_cl.py \ simRun/Sim/__init__.py \ simRun/Sim/initialization.py \ simRun/Sim/matrixGen.py \ - simRun/Sim/computeSources.py \ - simRun/Sim/fsEvolution.py \ + simRun/Sim/BEMsolver.py \ + simRun/Sim/evolution.py \ simPost/__init__.py \ simPost/TaskPanel.py \ simPost/TaskPanel.ui diff --git a/src/Mod/Ship/SimInstance.py b/src/Mod/Ship/SimInstance.py index 0e8f6db2a..638c17612 100644 --- a/src/Mod/Ship/SimInstance.py +++ b/src/Mod/Ship/SimInstance.py @@ -62,19 +62,38 @@ class FreeSurfaceFace: self.area = area class ShipSimulation: - def __init__(self, obj, fsMeshData, waves): + def __init__(self, obj, fsMeshData, waves, error): """ Creates a new simulation instance on active document. @param obj Created Part::FeaturePython object. + @param h Sea water level. @param fsMeshData [L,B,N] Free surface mesh data, with lenght (x), Beam (y) and desired number of points. - @param waves [[A,T,phi,heading],] Waves involved + @param waves [[A,T,phi,heading],] Waves involved. + @param error Relation between the minimum and the maximum Green's function values. """ # Add uniqueness property to identify Tank instances tooltip = str(QtGui.QApplication.translate("Ship","True if is a valid ship simulation instance", None,QtGui.QApplication.UnicodeUTF8)) obj.addProperty("App::PropertyBool","IsShipSimulation","ShipSimulation", tooltip).IsShipSimulation=True + # Store general data + tooltip = str(QtGui.QApplication.translate("Ship","Free surface length in the x direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyFloat","L","ShipSimulation", tooltip).L=fsMeshData[0] + tooltip = str(QtGui.QApplication.translate("Ship","Free surface length in the y direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyFloat","B","ShipSimulation", tooltip).B=fsMeshData[1] + tooltip = str(QtGui.QApplication.translate("Ship","Free surface number of elements at x direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyInteger","FS_Nx","ShipSimulation", tooltip).FS_Nx=fsMeshData[2] + tooltip = str(QtGui.QApplication.translate("Ship","Free surface number of elements at y direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyInteger","FS_Ny","ShipSimulation", tooltip).FS_Ny=fsMeshData[3] + tooltip = str(QtGui.QApplication.translate("Ship","Relative error of the Green's function", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyFloat","error","ShipSimulation", tooltip).error=error # Compute free surface mesh self.createFSMesh(obj,fsMeshData) + self.createVirtualFS(obj,fsMeshData,error) self.computeWaves(obj,waves) # Store waves tooltip = str(QtGui.QApplication.translate("Ship","Waves (Amplitude,period,phase)", @@ -112,8 +131,8 @@ class ShipSimulation: def createFSMesh(self, obj, fsMeshData): """ Create or modify free surface mesh. @param obj Created Part::FeaturePython object. - @param fsMeshData [L,B,N] Free surface mesh data, with lenght - (x), Beam (y) and desired number of points. + @param fsMeshData [L,B,Nx,Ny] Free surface mesh data, with lenght + (x), Breath (y) and desired number of points at each direction. """ # Study input object try: @@ -130,29 +149,17 @@ class ShipSimulation: FreeCAD.Console.PrintError(msg + '\n') return # Get areas and number of elements per direction - L = fsMeshData[0] - B = fsMeshData[1] - N = fsMeshData[2] - A = L*B + L = fsMeshData[0] + B = fsMeshData[1] + nx = fsMeshData[2] + ny = fsMeshData[3] + N = nx*ny + A = L*B area = A/N - l = sqrt(area) - b = sqrt(area) - nx = int(round(L / l)) - ny = int(round(B / b)) + l = L/nx + b = B/ny # Start data fields if not already exist props = obj.PropertiesList - try: - props.index("FS_Nx") - except ValueError: - tooltip = str(QtGui.QApplication.translate("Ship","Free surface number of elements at x direction", - None,QtGui.QApplication.UnicodeUTF8)) - obj.addProperty("App::PropertyInteger","FS_Nx","ShipSimulation", tooltip).FS_Nx=0 - try: - props.index("FS_Ny") - except ValueError: - tooltip = str(QtGui.QApplication.translate("Ship","Free surface number of elements at y direction", - None,QtGui.QApplication.UnicodeUTF8)) - obj.addProperty("App::PropertyInteger","FS_Ny","ShipSimulation", tooltip).FS_Ny=0 try: props.index("FS_Position") except ValueError: @@ -172,6 +179,8 @@ class ShipSimulation: None,QtGui.QApplication.UnicodeUTF8)) obj.addProperty("App::PropertyVectorList","FS_Normal","ShipSimulation", tooltip).FS_Normal=[] # Fill data + obj.L = L + obj.B = B obj.FS_Nx = nx obj.FS_Ny = ny pos = [] @@ -186,6 +195,68 @@ class ShipSimulation: obj.FS_Area = areas[:] obj.FS_Normal = normal[:] + def createVirtualFS(self, obj, fsMeshData, error): + """ Computes the number of required extended free surfaces. + @param obj Created Part::FeaturePython object. + @param fsMeshData [L,B,Nx,Ny] Free surface mesh data, with lenght + (x), Breath (y) and desired number of points at each direction. + @param error Relation between the minimum and the maximum Green's function values. + """ + # Study input object + try: + props = obj.PropertiesList + props.index("IsShipSimulation") + if not obj.IsShipSimulation: + msg = QtGui.QApplication.translate("ship_console", "Object is not a valid ship simulation", + None,QtGui.QApplication.UnicodeUTF8) + FreeCAD.Console.PrintError(msg + '\n') + return + except ValueError: + msg = QtGui.QApplication.translate("ship_console", "Object is not a ship simulation", + None,QtGui.QApplication.UnicodeUTF8) + FreeCAD.Console.PrintError(msg + '\n') + return + # Get dimensions of the elements + L = fsMeshData[0] + B = fsMeshData[1] + nx = fsMeshData[2] + ny = fsMeshData[3] + dx = L / nx + dy = B / ny + # Compute maximum Green's function considering flat free surface + Gmax = dx*asinh(dy/dx) + dy*asinh(dx/dy) + # Locate the distance (number of free surface) to get the minimum required value + Gmin = error*Gmax + x = (L-dx)/2.0 + Nx = 0 + G = Gmin + 1.0 + while(G > Gmin): + x = x + L + Nx = Nx + 1 + G = 1.0 / (4.0*pi * x) + y = (B-dy)/2.0 + Ny = 0 + G = Gmin + 1.0 + while(G > Gmin): + y = y + L + Ny = Ny + 1 + G = 1.0 / (4.0*pi * y) + # Register computed data + try: + props.index("Sea_Nx") + except ValueError: + tooltip = str(QtGui.QApplication.translate("Ship","Number of repetitions of the free surface at x direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyInteger","Sea_Nx","ShipSimulation", tooltip).Sea_Nx=0 + try: + props.index("Sea_Ny") + except ValueError: + tooltip = str(QtGui.QApplication.translate("Ship","Number of repetitions of the free surface at y direction", + None,QtGui.QApplication.UnicodeUTF8)) + obj.addProperty("App::PropertyInteger","Sea_Ny","ShipSimulation", tooltip).Sea_Ny=0 + obj.Sea_Nx = Nx + obj.Sea_Ny = Ny + def computeWaves(self, obj, waves): """ Add waves effect to free surface mesh positions. @param obj Created Part::FeaturePython object. @@ -215,21 +286,14 @@ class ShipSimulation: """ nx = obj.FS_Nx ny = obj.FS_Ny - mesh = FSMesh(obj) - # Create BSpline surface - surf = Part.BSplineSurface() - for i in range(1,nx-1): - u = i / float(nx-1) - surf.insertUKnot(u,i,0.000001) - for i in range(1,ny-1): - v = i / float(ny-1) - surf.insertVKnot(v,i,0.000001) + mesh = FSMesh(obj) + surf = Part.BSplineSurface() + pos = [] for i in range(0,nx): + pos.append([]) for j in range(0,ny): - u = i / float(nx-1) - v = j / float(ny-1) - point = mesh[i][j].pos - surf.movePoint(u,v,point,i+1,i+1,j+1,j+1) + pos[i].append(mesh[i][j].pos) + surf.interpolate(pos) return surf.toShape() class ViewProviderShipSimulation: @@ -694,3 +758,4 @@ def FSMesh(obj, recompute=False): obj.FS_Normal[j + i*ny] = faces[i][j].normal obj.FS_Area[j + i*ny] = faces[i][j].area return faces + diff --git a/src/Mod/Ship/TankInstance.py b/src/Mod/Ship/TankInstance.py index 8bb084bf9..d85db4856 100644 --- a/src/Mod/Ship/TankInstance.py +++ b/src/Mod/Ship/TankInstance.py @@ -735,9 +735,12 @@ def tankWeight(obj, angles=Vector(0.0,0.0,0.0), cor=Vector(0.0,0.0,0.0)): z = z0 + dz dx = bbox.XMax-bbox.XMin dy = bbox.YMax-bbox.YMin - box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) - fluid = s.common(box) - vol = fluid.Volume + try: + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + vol = fluid.Volume + except: + vol = 0.0 W[0] = W[0] + vol*obj.Density # Compute fluid solid in rotated position (non linear rotation # are ussually computed as Roll -> Pitch -> Yaw). @@ -755,9 +758,12 @@ def tankWeight(obj, angles=Vector(0.0,0.0,0.0), cor=Vector(0.0,0.0,0.0)): while(abs(vol - v) > Error): z = z + (vol - v) / (dx*dy) dz = z - z0 - box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) - fluid = s.common(box) - v = fluid.Volume + try: + box = Part.makeBox(3.0*(dx), 3.0*(dy), (z1-z0)+dz, Vector(bbox.XMin-dx, bbox.YMin-dy, bbox.ZMin-(z1-z0))) + fluid = s.common(box) + v = fluid.Volume + except: + v = 0.0 if(abs(vol - v) / (dx*dy) <= 0.000001): break # Add fluid moments diff --git a/src/Mod/Ship/resources/opencl/jacobi.cl b/src/Mod/Ship/resources/opencl/jacobi.cl new file mode 100644 index 000000000..88eadc992 --- /dev/null +++ b/src/Mod/Ship/resources/opencl/jacobi.cl @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011, 2012 * + * Jose Luis Cercos Pita * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License (LGPL) * + * as published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * for detail see the LICENCE text file. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + */ + +/** Compute residuals of the solution stimator for a linear system. + * @param A Linear system matrix. + * @param B Linear system independent term. + * @param X Solution estimation. + * @param R Residuals. + * @param n Linear system dimension. + */ +__kernel void r(__global float* A, + __global float* B, + __global float* X, + __global float* R, + unsigned int n) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + unsigned int j; + if(i >= n) + return; + // Evaluate the row + R[i] = B[i]; + for(j=0;j= n) + return; + // Evaluate the row + X[i] = B[i]; + for(j=0;j * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License (LGPL) * + * as published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * for detail see the LICENCE text file. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + */ + +/** Get matrix column. + * @param A Linear system matrix. + * @param v Column vector (output). + * @param col Column index. + * @param n Linear system dimension. + */ +__kernel void column(__global float* A, + __global float* v, + unsigned int col, + unsigned int n) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + if(i >= n) + return; + v[i] = A[i + col*n]; +} + +/** Performs matrix column product by a constant. + * @param A Linear system matrix. + * @param c Constant. + * @param col Column index. + * @param n Linear system dimension. + */ +__kernel void prod_c_column(__global float* A, + float c, + unsigned int col, + unsigned int n) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + if(i >= n) + return; + A[i + col*n] *= c; +} + +/** Compute residuals of the solution stimator for a linear system. + * @param A Linear system matrix. + * @param B Linear system independent term. + * @param X Solution estimation. + * @param R Residuals. + * @param n Linear system dimension. + */ +__kernel void r(__global float* A, + __global float* B, + __global float* X, + __global float* R, + unsigned int n) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + unsigned int j; + if(i >= n) + return; + // Evaluate the row + R[i] = B[i]; + for(j=0;j= n) + return; + // Evaluate the row + Y[i] = 0.f; + for(j=0;j= n) + return; + // Evaluate the row + Y[i] = 0.f; + for(j=0;j= n) + return; + // Evaluate the row + u[i] = - alpha * u0[i]; + for(j=0;j= n) + return; + // Evaluate the row + v[i] = - beta * v0[i]; + for(j=0;j * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License (LGPL) * + * as published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * for detail see the LICENCE text file. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + */ + +#ifndef M_PI + #define M_PI 3.14159265f +#endif + +#ifndef _NG_ + #define _NG_ 16 +#endif + +/** Compute \$G_{ab}\$ effect: \n + * \$ G_{ab} = \frac{1}{4 \pi \vert \mathbf{r} \vert } \$ + * @param r Union vector \$ \mathbf{r}_{ab} \$ + */ +float G_val(float4 r) +{ + return 1.f / ( 4.f*M_PI * length(r) ); +} + +/** Compute \$H_{ab}\$ effect: \n + * \$ H_{ab} = \frac{\mathbf{r}}{4 \pi \vert \mathbf{r} \vert^3} \cdot n_b \$ + * @param r Union vector \$ \mathbf{r}_{ab} \$ + * @param n Element normal \$ n_b \$ + */ +float H_val(float4 r, + float4 n) +{ + return - dot(r,n) / (4.f*M_PI * pow(dot(r,r),1.5f)); +} + +/** Computes z coordinate due to the waves superposition + * for a desired position. + * @param w Array of waves. + * @param p Point to compute. + * @param t Simulation time. + * @param nW Number of waves. + * @return z coordinate. + */ +float waves_z(__global float4* w, float4 p, float t, unsigned int nW) +{ + /* + return 0.f; + */ + + unsigned int i; + float z = 0.f; + for(i=0;i -0.5*L) && + (P[i*3+j].x < 0.5*L) && + (P[i*3+j].y > -0.5*B) && + (P[i*3+j].y < 0.5*B)) + P[i*3+j].z = positions[(I-1+i)*ny + (J-1+j)].z; + else + P[i*3+j].z = waves_z(w, P[i*3+j], t, nW); + P[i*3+j].w = 0.f; + } + } + // Get SPline surface coeffs + K[0] = P[0].z; // k_{0} + K[1] = 4*P[3].z - P[6].z - 3*P[0].z; // k_{u} + K[2] = 4*P[1].z - P[2].z - 3*P[0].z; // k_{v} + K[3] = P[8].z - 4*P[7].z + 3*P[6].z + + 3*P[2].z - 12*P[1].z + 9*P[0].z + + -4*P[5].z + 16*P[4].z - 12*P[3].z; // k_{uv} + K[4] = 2*P[6].z + 2*P[0].z - 4*P[3].z; // k_{uu} + K[5] = 2*P[2].z + 2*P[0].z - 4*P[1].z; // k_{vv} + K[6] = -2*P[8].z + 8*P[7].z - 6*P[6].z + + -2*P[2].z + 8*P[1].z - 6*P[0].z + + 4*P[5].z - 16*P[4].z + 12*P[3].z; // k_{uuv} + K[7] = -2*P[8].z + 4*P[7].z - 2*P[6].z + + -6*P[2].z + 12*P[1].z - 6*P[0].z + + 8*P[5].z - 16*P[4].z + 8*P[3].z; // k_{uuv} + K[8] = 4*P[8].z - 8*P[7].z + 4*P[6].z + + 4*P[2].z - 8*P[1].z + 4*P[0].z + + -8*P[5].z + 16*P[4].z - 8*P[3].z; // k_{uuvv} + // Loop around the point p collecting the integral + float2 gh; + gh.x = 0.0f; + gh.y = -0.5f; + for(i=0;i<_NG_;i++){ + for(j=0;j<_NG_;j++){ + float4 p_a; + float u,v; + p_a.x = positions[I*ny + J].x - 0.5f*dx + (i+0.5f)*Dx; + p_a.y = positions[I*ny + J].y - 0.5f*dy + (j+0.5f)*Dy; + u = (p_a.x - P[0].x) / (P[6].x - P[0].x); + v = (p_a.y - P[0].y) / (P[2].y - P[0].y); + p_a.z = K[0] + K[1]*u + K[2]*v + K[3]*u*v + + K[4]*u*u + K[5]*v*v + K[6]*u*u*v + + K[7]*u*v*v + K[8]*u*u*v*v; + p_a.w = 1.f; + gh.x += G_val(p_a - p)*Dx*Dy; + // For some reason H is not well integrated + // gh.y += H_val(p_a - p, n)*Dx*Dy; + } + } + return gh; +} + +/** Compute Linear system matrix. Desingularized sources must taken into account. + * @param A Linear system matrix. + * @param B Independent term for velocity potentials. + * @param positions Elements points. + * @param areas Elements area. + * @param normals Elements normals. + * @param p Velocity potentials. + * @param dp Acceleration potentials. + * @param waves Array of waves data (Amplitude,period,phase,heading) + * @param l Free surface length in the x direction. + * @param b Free surface length in the y direction. + * @param dx Distance between element centers in the x direction. + * @param dy Distance between element centers in the x direction. + * @param t Simulation time. + * @param nx Free surface points in the x direction. + * @param ny Free surface points in the y direction. + * @param nFS Number of points in the free surface. + * @param nB Number of points in the body (ignored yet, should be 0). + * @param n Total number of points. + * @param nSeax Number of repetitions of the free surface in the x direction. + * @param nSeay Number of repetitions of the free surface in the y direction. + * @param nW Number of waves. + */ +__kernel void matrixGen(__global float* A, + __global float* B, + __global float4* positions, + __global float* areas, + __global float4* normals, + __global float* p, + __global float* dp, + __global float4* waves, + float l, + float b, + float dx, + float dy, + float t, + unsigned int nx, + unsigned int ny, + unsigned int nFS, + unsigned int nB, + unsigned int n, + int nSeax, + int nSeay, + unsigned int nW) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + unsigned int j; + int I,J; + if(i >= n) + return; + // Get the point where we want to evaluate + float4 p_a = positions[i]; + // Evaluate the row + B[i] = 0.f; + for(j=0;j * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License (LGPL) * + * as published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * for detail see the LICENCE text file. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + */ + +/** Compute residuals of the solution stimator for a linear system. + * @param A Linear system matrix. + * @param B Linear system independent term. + * @param X Solution estimation. + * @param R Residuals. + * @param n Linear system dimension. + */ +__kernel void r(__global float* A, + __global float* B, + __global float* X, + __global float* R, + unsigned int n) +{ + // find position in global arrays + unsigned int i = get_global_id(0); + unsigned int j; + if(i >= n) + return; + // Evaluate the row + R[i] = B[i]; + for(j=0;j= n) + return; + // Evaluate the row + Y[i] = 0.f; + for(j=0;j= n) + return; + // Evaluate the row + x[i] = x0[i] + Ar_r / Ar_Ar * r[i]; +} + diff --git a/src/Mod/Ship/shipHydrostatics/Tools.py b/src/Mod/Ship/shipHydrostatics/Tools.py index 8e4faab28..09123204f 100644 --- a/src/Mod/Ship/shipHydrostatics/Tools.py +++ b/src/Mod/Ship/shipHydrostatics/Tools.py @@ -253,6 +253,10 @@ def FloatingArea(ship, draft, trim): p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0) box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p) # Compute common part with ship + maxX = bbox.XMin + minX = bbox.XMax + maxY = bbox.YMin + minY = bbox.YMax for s in shape.Solids: # Get solids intersection try: @@ -322,13 +326,13 @@ def mainFrameCoeff(ship, draft): @param draft Draft. @return Main frame coefficient """ - cm = 0.0 - maxY = 0.0 - minY = 0.0 + cm = 0.0 + maxY = 0.0 + minY = 0.0 # We will take a duplicate of ship shape in order to place it shape = ship.Shape.copy() shape.translate(Vector(0.0,0.0,-draft)) - x = 0.0 + x = 0.0 area = 0.0 # Now we need to know the x range of values bbox = shape.BoundBox @@ -339,6 +343,8 @@ def mainFrameCoeff(ship, draft): B = bbox.YMax - bbox.YMin p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0) box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin + 1.0, p) + maxY = bbox.YMin + minY = bbox.YMax # Compute common part with ship for s in shape.Solids: # Get solids intersection @@ -367,7 +373,7 @@ def mainFrameCoeff(ship, draft): # Valid face, compute area area = area + f.Area maxY = max(maxY, faceBounds.YMax) - minY = max(minY, faceBounds.YMin) + minY = min(minY, faceBounds.YMin) # Destroy last object generated App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name) dy = maxY - minY diff --git a/src/Mod/Ship/simCreate/TaskPanel.py b/src/Mod/Ship/simCreate/TaskPanel.py index 7e200532b..1f3592709 100644 --- a/src/Mod/Ship/simCreate/TaskPanel.py +++ b/src/Mod/Ship/simCreate/TaskPanel.py @@ -49,9 +49,10 @@ class TaskPanel: head = item.text().toFloat()[0] w.append([A,T,phi,head]) obj = App.ActiveDocument.addObject("Part::FeaturePython","ShipSimulation") - sim = SimInstance.ShipSimulation(obj, - [form.length.value(), form.beam.value(), form.n.value()], - w) + sim = SimInstance.ShipSimulation(obj, + [form.fsL.value(), form.fsB.value(), form.fsNx.value(), form.fsNy.value()], + w, + form.error.value()) SimInstance.ViewProviderShipSimulation(obj.ViewObject) return True @@ -80,21 +81,25 @@ class TaskPanel: pass def setupUi(self): - mw = self.getMainWindow() - form = mw.findChild(QtGui.QWidget, "TaskPanel") - form.length = form.findChild(QtGui.QDoubleSpinBox, "Length") - form.beam = form.findChild(QtGui.QDoubleSpinBox, "Beam") - form.n = form.findChild(QtGui.QSpinBox, "N") - form.waves = form.findChild(QtGui.QTableWidget, "Waves") + mw = self.getMainWindow() + form = mw.findChild(QtGui.QWidget, "TaskPanel") + form.fsL = form.findChild(QtGui.QDoubleSpinBox, "Length") + form.fsB = form.findChild(QtGui.QDoubleSpinBox, "Beam") + form.fsNx = form.findChild(QtGui.QSpinBox, "Nx") + form.fsNy = form.findChild(QtGui.QSpinBox, "Ny") + form.error = form.findChild(QtGui.QDoubleSpinBox, "Error") + form.waves = form.findChild(QtGui.QTableWidget, "Waves") self.form = form # Initial values if self.initValues(): return True self.retranslateUi() # Connect Signals and Slots - QtCore.QObject.connect(form.length, QtCore.SIGNAL("valueChanged(double)"), self.onFS) - QtCore.QObject.connect(form.beam, QtCore.SIGNAL("valueChanged(double)"), self.onFS) - QtCore.QObject.connect(form.n, QtCore.SIGNAL("valueChanged(int)"), self.onFS) + QtCore.QObject.connect(form.fsL, QtCore.SIGNAL("valueChanged(double)"), self.onFS) + QtCore.QObject.connect(form.fsB, QtCore.SIGNAL("valueChanged(double)"), self.onFS) + QtCore.QObject.connect(form.fsNx, QtCore.SIGNAL("valueChanged(int)"), self.onFS) + QtCore.QObject.connect(form.fsNy, QtCore.SIGNAL("valueChanged(int)"), self.onFS) + QtCore.QObject.connect(form.error, QtCore.SIGNAL("valueChanged(double)"), self.onError) QtCore.QObject.connect(form.waves,QtCore.SIGNAL("cellChanged(int,int)"),self.onWaves); def getMainWindow(self): @@ -120,14 +125,12 @@ class TaskPanel: None,QtGui.QApplication.UnicodeUTF8)) self.form.findChild(QtGui.QGroupBox, "FSDataBox").setTitle(QtGui.QApplication.translate("shipsim_create","Free surface", None,QtGui.QApplication.UnicodeUTF8)) - self.form.findChild(QtGui.QLabel, "LengthLabel").setText(QtGui.QApplication.translate("shipsim_create","Length", - None,QtGui.QApplication.UnicodeUTF8)) - self.form.findChild(QtGui.QLabel, "BeamLabel").setText(QtGui.QApplication.translate("shipsim_create","Breadth", - None,QtGui.QApplication.UnicodeUTF8)) - self.form.findChild(QtGui.QLabel, "NLabel").setText(QtGui.QApplication.translate("shipsim_create","Number of points", - None,QtGui.QApplication.UnicodeUTF8)) self.form.findChild(QtGui.QGroupBox, "WavesDataBox").setTitle(QtGui.QApplication.translate("shipsim_create","Waves", None,QtGui.QApplication.UnicodeUTF8)) + self.form.findChild(QtGui.QGroupBox, "OtherBox").setTitle(QtGui.QApplication.translate("shipsim_create","Other", + None,QtGui.QApplication.UnicodeUTF8)) + self.form.findChild(QtGui.QLabel, "ErrorLabel").setText(QtGui.QApplication.translate("shipsim_create","Relative error", + None,QtGui.QApplication.UnicodeUTF8)) labels = [] labels.append(QtGui.QApplication.translate("shipsim_create","Amplitude", None,QtGui.QApplication.UnicodeUTF8) + " [m]") @@ -138,6 +141,27 @@ class TaskPanel: labels.append(QtGui.QApplication.translate("shipsim_create","Heading", None,QtGui.QApplication.UnicodeUTF8) + " [deg]") self.form.waves.setHorizontalHeaderLabels(labels) + # Set some tooltips + tooltip = QtGui.QApplication.translate("shipsim_create","Free surface length on x direction", + None,QtGui.QApplication.UnicodeUTF8) + self.form.findChild(QtGui.QLabel, "LengthLabel").setToolTip(tooltip) + self.form.findChild(QtGui.QDoubleSpinBox, "Length").setToolTip(tooltip) + tooltip = QtGui.QApplication.translate("shipsim_create","Free surface length on y direction", + None,QtGui.QApplication.UnicodeUTF8) + self.form.findChild(QtGui.QLabel, "BeamLabel").setToolTip(tooltip) + self.form.findChild(QtGui.QDoubleSpinBox, "Beam").setToolTip(tooltip) + tooltip = QtGui.QApplication.translate("shipsim_create","Number of nodes on x direction. Take into acount the following considerations:\n1.\tNodes must have an aspect ratio as near to 1,0 as possible, so this values must\n\taccomplish approximately that Nx/Ny = L/B\n3.\tThe linear system matrix generated will be of dimensions NxN, where\n\tN = Nx*Ny\n\tSo be mindful with the values selected and computer capabilities.", + None,QtGui.QApplication.UnicodeUTF8) + self.form.findChild(QtGui.QLabel, "NxLabel").setToolTip(tooltip) + self.form.findChild(QtGui.QSpinBox, "Nx").setToolTip(tooltip) + tooltip = QtGui.QApplication.translate("shipsim_create","Number of nodes on y direction. Take into acount the following considerations:\n1.\tNodes must have an aspect ratio as near to 1,0 as possible, so this values must\n\taccomplish approximately that Nx/Ny = L/B\n3.\tThe linear system matrix generated will be of dimensions NxN, where\n\tN = Nx*Ny\n\tSo be mindful with the values selected and computer capabilities.", + None,QtGui.QApplication.UnicodeUTF8) + self.form.findChild(QtGui.QLabel, "NyLabel").setToolTip(tooltip) + self.form.findChild(QtGui.QSpinBox, "Ny").setToolTip(tooltip) + tooltip = QtGui.QApplication.translate("shipsim_create","Relation between the minimum value of the Green's function (fartest point) and the maximum one.\nThis variable set the number of times that the Free surface will be virtually repeated.\nLower values may imply too much repeated free surfaces with a significant cost.", + None,QtGui.QApplication.UnicodeUTF8) + self.form.findChild(QtGui.QLabel, "ErrorLabel").setToolTip(tooltip) + self.form.findChild(QtGui.QDoubleSpinBox, "Error").setToolTip(tooltip) def onFS(self, value): """ Method called when free surface data is changed. @@ -145,6 +169,12 @@ class TaskPanel: """ pass + def onError(self, value): + """ Method called when sea data is changed. + @param value Changed value. + """ + pass + def onWaves(self, row, column): """ Method called when waves data is changed. @param row Affected row. diff --git a/src/Mod/Ship/simCreate/TaskPanel.ui b/src/Mod/Ship/simCreate/TaskPanel.ui index 107d2a16d..6dcc376a5 100644 --- a/src/Mod/Ship/simCreate/TaskPanel.ui +++ b/src/Mod/Ship/simCreate/TaskPanel.ui @@ -6,7 +6,7 @@ 0 0 - 269 + 386 384 @@ -42,7 +42,7 @@ 0 - 128 + 0 @@ -55,11 +55,11 @@ false - - QLayout::SetDefaultConstraint - + + 2 + QLayout::SetMinimumSize @@ -78,15 +78,27 @@ - Length + L + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 1 + + 1.000000000000000 + 1000000.000000000000000 @@ -98,37 +110,30 @@ - - - - - - QLayout::SetMinimumSize - - - 10 - - - 0 - - - 10 - - - 0 - - Beam + B + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 1 + + 1.000000000000000 + 1000000.000000000000000 @@ -160,22 +165,60 @@ 0 - + - Number of points + Nx - + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 3 + + + 10000000 + + + 25 + + + + + + + Ny + + + + + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 1 - 1000000000 + 1000000 - 1000 + 25 @@ -260,6 +303,48 @@ + + + + Other + + + + + + + + Relative error + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 0.000100000000000 + + + 1.000000000000000 + + + 0.001000000000000 + + + 0.001000000000000 + + + + + + + + diff --git a/src/Mod/Ship/simRun/Sim/BEMsolver.py b/src/Mod/Ship/simRun/Sim/BEMsolver.py new file mode 100644 index 000000000..da6932483 --- /dev/null +++ b/src/Mod/Ship/simRun/Sim/BEMsolver.py @@ -0,0 +1,55 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np +import scipy.linalg as la +import FreeCAD + +grav=9.81 + +class simBEMSolver: + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue + + def execute(self, bem): + """ Compute potential unknow data (gradients for free surface, and + potentials for the other ones). + @param bem Boundary Element Method instance. + """ + [bem['Ap'], residues, rank, s] = la.lstsq(bem['A'], bem['B']) + if(rank < bem['N']): + FreeCAD.Console.PrintError("\t\t[Sim]: Solving velocity potentials.\n") + FreeCAD.Console.PrintError("\t\t\tEffective rank of linear system matrix is %i (N = %i)\n" % (rank, bem['N'])) + [bem['Adp'], residues, rank, s] = la.lstsq(bem['A'], bem['dB']) + if(rank < bem['N']): + FreeCAD.Console.PrintError("\t\t[Sim]: Solving acceleration potentials.\n") + FreeCAD.Console.PrintError("\t\t\tEffective rank of linear system matrix is %i (N = %i)\n" % (rank, bem['N'])) + diff --git a/src/Mod/Ship/simRun/Sim/__init__.py b/src/Mod/Ship/simRun/Sim/__init__.py index aabf4a621..6a185fde4 100644 --- a/src/Mod/Ship/simRun/Sim/__init__.py +++ b/src/Mod/Ship/simRun/Sim/__init__.py @@ -23,5 +23,5 @@ from initialization import * from matrixGen import * -from computeSources import * -from fsEvolution import * +from BEMsolver import * +from evolution import * diff --git a/src/Mod/Ship/simRun/Sim/evolution.py b/src/Mod/Ship/simRun/Sim/evolution.py new file mode 100644 index 000000000..1108cefe1 --- /dev/null +++ b/src/Mod/Ship/simRun/Sim/evolution.py @@ -0,0 +1,304 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np + +grav=9.81 + +class simEvolution: + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue + + def executeRK4(self, x, dx, p, dp, pos, vel, phi, dphi, fs, sea, body, waves, dt, t, stage): + """ Compute free surface RK4 stage evolution process (valid for stages 1,2 and 3). + @param x Output free surface z coordinates. + @param dx Output free surface z coordinates variation (dz/dt). + @param p Output potentials. + @param dp Output potentials variation (dphi/dt). + @param pos Input free surface positions. + @param vel Input free surface velocities. + @param phi Input potentials. + @param dphi Input potentials variation (dphi/dt). + @param fs Free surface instance. + @param sea Sea instance. + @param body Body instance. + @param waves Waves instance. + @param dt Time step. + @param t Actual time (without adding dt). + @param stage Runge-Kutta4 stage. + @return Input variables evoluted one time step. + """ + # -------------------------------------------- + # Only free surface + # -------------------------------------------- + h = fs['h'] + nx = fs['Nx'] + ny = fs['Ny'] + nF = nx*ny + factor = 0.5 + if stage > 2: + factor = 1. + for i in range(0,nx): + for j in range(0,ny): + x[i,j] = np.copy(pos[i,j][2]) + dx[i,j] = np.copy(vel[i,j][2]) + x[i,j] = x[i,j] + factor*dt*dx[i,j] + p[i*ny+j] = np.copy(phi[i*ny+j]) + dp[i*ny+j] = np.copy(dphi[i*ny+j]) + p[i*ny+j] = p[i*ny+j] + factor*dt*dp[i*ny+j] + # Impose values at beach (far free surface) + nbx = fs['Beachx'] + nby = fs['Beachy'] + for i in range(0,nx): + for j in range(0,nby) + range(ny-nby,ny): + [x[i,j],dx[i,j],p[i*ny+j],dp[i*ny+j]] = self.beach(pos[i,j], waves, factor*dt, t) + for j in range(0,ny): + for i in range(0,nbx) + range(nx-nbx,nx): + [x[i,j],dx[i,j],p[i*ny+j],dp[i*ny+j]] = self.beach(pos[i,j], waves, factor*dt, t) + # -------------------------------------------- + # Sea boundaries, where potentials are fixed. + # We use the gradient projected over normal, + # see initialization for more details about + # this. + # -------------------------------------------- + ids = ['front','back','left','right','bottom'] + i0 = fs['N'] + for index in ids: + s = sea[index] + nx = s['Nx'] + ny = s['Ny'] + for i in range(0,nx): + for j in range(0,ny): + p[i0 + i*ny+j] = 0. + dp[i0 + i*ny+j] = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = s['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + normal = s['normal'][i,j] + hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h) + factor = np.dot(normal,np.array([np.cos(heading), np.sin(heading), 0.])) + amp = frec*A*np.sin(k*l - frec*(t+factor*dt) + phase)*hfact + p[i0 + i*ny+j] = p[i0 + i*ny+j] + factor*amp + amp = - grav*A*k*np.cos(k*l - frec*(t+factor*dt) + phase)*hfact + dp[i0 + i*ny+j] = dp[i0 + i*ny+j] + factor*amp + i0 = i0 + s['N'] + + def execute(self, dx1, dx2, dx3, dp1, dp2, dp3, fs, sea, body, waves, bem, dt, t): + """ Compute free surface evolution process (execute it on RK4 last stage). + @param dx1 Input free surface positions variation on stage 1. + @param dx2 Input free surface positions variation on stage 2. + @param dx3 Input free surface positions variation on stage 3. + @param dp1 Input free surface potentials variation on stage 1. + @param dp2 Input free surface potentials variation on stage 2. + @param dp3 Input free surface potentials variation on stage 3. + @param fs Free surface instance. + @param sea Sea instance. + @param body Body instance. + @param waves Waves instance. + @param bem Boundary Element Method instance. + @param dt Time step. + @param t Actual time (without adding dt). + @param stage Runge-Kutta4 stage. + @return Input variables evoluted one time step. + """ + h = fs['h'] + nx = fs['Nx'] + ny = fs['Ny'] + nF = nx*ny + for i in range(0,nx): + for j in range(0,ny): + # In this stage dx4 and dp4 are directly known from the previous + # stage. + dx4 = fs['vel'][i,j][2] + dp4 = bem['dp4'][i*ny+j] + # And we only need to apply the integration scheme + fs['pos'][i,j][2] = fs['pos'][i,j][2] + dt/6. * (dx1[i,j] + 2.*dx2[i,j] + 2.*dx3[i,j] + dx4) + bem['p4'][i*ny+j] = bem['p4'][i*ny+j] + dt/6. * (dp1[i*ny+j] + 2.*dp2[i*ny+j] + 2.*dp3[i*ny+j] + dp4) + # In order to can apply the boundary condition at the free surface + # at the end of this RK4 stage, we need to store eta in a variable. + # x1 is safe because will be over written at the start of next + # time step. + fs['x1'][i,j] = fs['pos'][i,j][2] + # Impose values at beach (far free surface) + nbx = fs['Beachx'] + nby = fs['Beachy'] + for i in range(0,nx): + for j in range(0,nby) + range(ny-nby,ny): + [x,dummy,p,dummy] = self.beach(fs['pos'][i,j], waves, dt, t) + fs['pos'][i,j][2] = x + bem['p4'][i*ny+j] = p + fs['x1'][i,j] = fs['pos'][i,j][2] + for j in range(0,ny): + for i in range(0,nbx) + range(nx-nbx,nx): + [x,dummy,p,dummy] = self.beach(fs['pos'][i,j], waves, dt, t) + fs['pos'][i,j][2] = x + bem['p4'][i*ny+j] = p + fs['x1'][i,j] = fs['pos'][i,j][2] + # -------------------------------------------- + # Sea boundaries, where potentials are fixed. + # We use the gradient projected over normal, + # see initialization for more details about + # this. + # -------------------------------------------- + ids = ['front','back','left','right','bottom'] + i0 = fs['N'] + p = bem['p4'] + dp = bem['dp4'] + for index in ids: + s = sea[index] + nx = s['Nx'] + ny = s['Ny'] + for i in range(0,nx): + for j in range(0,ny): + p[i0 + i*ny+j] = 0. + dp[i0 + i*ny+j] = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = s['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + normal = s['normal'][i,j] + hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h) + factor = np.dot(normal,np.array([np.cos(heading), np.sin(heading), 0.])) + amp = frec*A*np.sin(k*l - frec*(t+factor*dt) + phase)*hfact + p[i0 + i*ny+j] = p[i0 + i*ny+j] + factor*amp + amp = - grav*A*k*np.cos(k*l - frec*(t+factor*dt) + phase)*hfact + dp[i0 + i*ny+j] = dp[i0 + i*ny+j] + factor*amp + i0 = i0 + s['N'] + + def executeFSBC(self, x, fs, sea, body, waves, bem, dt, t, stage): + """ Compute free surface boundary conditions in order to get + free surface points velocity and potentials acceleration for + the next RK4 stage. + @param x Free surface z coordinates. + @param fs Free surface instance. + @param sea Sea boundaries instance. + @param body Body instance. + @param waves Waves instance. + @param bem Boundary Element Method instance. + @param dt Time step. + @param t Actual time (without adding dt). + """ + nx = fs['Nx'] + ny = fs['Ny'] + nF = nx*ny + factor = 0.5 + if stage > 2: + factor = 1. + for i in range(0,nx): + for j in range(0,ny): + pos = np.copy(fs['pos'][i,j]) + pos[2] = x[i,j] + gradVal = bem['Ap'][i*ny+j] + normal = fs['normal'][i,j] + # v_z = dphi/dz - grad(phi)*grad(z) - U*dz/dx + dzdt = gradVal*normal[2] + # dphi/dt = - rho*g*z - 0.5*grad(phi)^2 + v_z*dphi/dz - p_0 - U*dphi/dx - dU/dt*x + dphidt = -grav*pos[2] - 0.5*np.dot(gradVal,gradVal) # + dzdt*gradVal*normal[2] + # We need to preserve data on free surface global + # velocity and potential values in order to use as + # input of the next RK4 stage + fs['vel'][i,j][2] = dzdt + bem['dp4'][i*ny+j] = dphidt + # Impose values at beach (far free surface) + nbx = fs['Beachx'] + nby = fs['Beachy'] + for i in range(0,nx): + for j in range(0,nby) + range(ny-nby,ny): + [dummy,dx,dummy,dp] = self.beach(fs['pos'][i,j], waves, factor*dt, t) + fs['vel'][i,j][2] = dx + bem['dp4'][i*ny+j] = dp + for j in range(0,ny): + for i in range(0,nbx) + range(nx-nbx,nx): + [dummy,dx,dummy,dp] = self.beach(fs['pos'][i,j], waves, factor*dt, t) + fs['vel'][i,j][2] = dx + bem['dp4'][i*ny+j] = dp + + def beach(self, pos, waves, dt, t): + """ Compute far free surface where only + incident waves can be taken into account. + @param pos Free surface position. + @param waves Waves instance. + @param dt Time step. + @param t Actual time (without adding dt). + @return Position, velocity, potential and potential acceleration + """ + h = waves['h'] + x = 0. + dx = 0. + p = 0. + dp = 0. + # Since values of the potencial, and this acceleration, + # depends on z, we need to compute first the positions. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + # hfact = np.sinh(k*(pos[2]+h)) / np.cosh(k*h) + hfact = 1.0 + amp = A*np.sin(k*l - frec*(t+dt) + phase)*hfact + x = x + amp + amp = - A*frec*np.cos(k*l - frec*(t+dt) + phase)*hfact + dx = dx + amp + # And now we can compute potentials. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + hfact = np.cosh(k*(x+h)) / np.cosh(k*h) + amp = - grav/frec*A*np.sin(k*l - frec*(t+dt) + phase)*hfact + p = p + amp + amp = grav*A*np.cos(k*l - frec*(t+dt) + phase)*hfact + dp = dp + amp + return [x,dx,p,dp] + diff --git a/src/Mod/Ship/simRun/Sim/initialization.py b/src/Mod/Ship/simRun/Sim/initialization.py index e89192c14..f7c58ad90 100644 --- a/src/Mod/Ship/simRun/Sim/initialization.py +++ b/src/Mod/Ship/simRun/Sim/initialization.py @@ -1,119 +1,327 @@ #*************************************************************************** -#* * -#* Copyright (c) 2011, 2012 * -#* Jose Luis Cercos Pita * -#* * +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * +#* USA * +#* * #*************************************************************************** # numpy import numpy as np +import FreeCAD grav=9.81 class simInitialization: - def __init__(self, FSmesh, waves, context=None, queue=None): - """ Constructor. - @param FSmesh Initial free surface mesh. - @param waves Considered simulation waves (A,T,phi,heading). - @param context OpenCL context where apply. Only for compatibility, - must be None. - @param queue OpenCL command queue. Only for compatibility, - must be None. - """ - self.context = context - self.queue = queue - self.loadData(FSmesh, waves) - self.execute() - # Compute time step - self.dt = 0.1 - for w in self.waves['data']: - if(self.dt > w[1]/200.0): - self.dt = w[1]/200.0 + def __init__(self, h, FSMesh, SeaMesh, waves, context=None, queue=None): + """ Constructor. + @param h Water height. + @param FSMesh Initial free surface mesh. + @param waves Considered simulation waves (A,T,phi,heading). + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue + self.loadData(h, FSMesh, SeaMesh, waves) + self.execute() + # Compute time step + self.dt = 0.1 + for w in self.waves['data']: + if(self.dt > w[1]/200.0): + self.dt = w[1]/200.0 - def loadData(self, FSmesh, waves): - """ Convert data to numpy format. - @param FSmesh Initial free surface mesh. - @param waves Considered simulation waves (A,T,phi,heading). - """ - nx = len(FSmesh) - ny = len(FSmesh[0]) - nW = len(waves) - # Mesh data - p = np.ndarray((nx,ny, 3), dtype=np.float32) - n = np.ndarray((nx,ny, 3), dtype=np.float32) - a = np.ndarray((nx,ny), dtype=np.float32) - phi = np.ndarray((nx,ny), dtype=np.float32) - Phi = np.ndarray((nx,ny), dtype=np.float32) - s = np.ndarray((nx,ny), dtype=np.float32) - ss = np.ndarray((nx,ny), dtype=np.float32) - for i in range(0, nx): - for j in range(0, ny): - pos = FSmesh[i][j].pos - normal = FSmesh[i][j].normal - area = FSmesh[i][j].area - p[i,j,0] = pos.x - p[i,j,1] = pos.y - p[i,j,2] = pos.z - n[i,j,0] = normal.x - n[i,j,1] = normal.y - n[i,j,2] = normal.z - a[i,j] = area - phi[i,j] = 0. - Phi[i,j] = 0. - s[i,j] = 0. - ss[i,j] = 0. - self.fs = {'Nx':nx, 'Ny':ny, 'pos':p, 'normal':n, 'area':a, \ - 'velPot':phi, 'accPot':Phi, 'velSrc':s, 'accSrc':ss} - # Waves data - w = np.ndarray((nW, 4), dtype=np.float32) - for i in range(0,nW): - w[i,0] = waves[i][0] - w[i,1] = waves[i][1] - w[i,2] = waves[i][2] - w[i,3] = waves[i][3] - self.waves = {'N':nW, 'data':w} - # Linear system matrix - nF = nx*ny - nB = 0 # No body for the moment - N = nx*ny + nB - self.A = np.ndarray((N, N), dtype=np.float32) + def loadData(self, h, FSMesh, SeaMesh, waves): + """ Convert data to numpy format. + @param FSMesh Initial free surface mesh. + @param waves Considered simulation waves (A,T,phi,heading). + """ + # Data will classified in four groups: + # Free surface: + # Is a key part of the simulation, so is + # separated from the rest of water involved + # elements. + # Sea: + # BEM method required a closed domain, so + # water floor and sides must be append, but + # are not a key objective of the simulation. + # Body: + # Is the main objective of the simulation. + # Waves: + # Data that is append as boundary condition. + # BEM: + # Used to solve the BEM problem and evolution. + + # -------------------------------------------- + # Free surface data + # N, Nx, Ny = Number of points in each + # direction + # pos = Positions + # vel = Velocities + # n = Normals + # area = Areas + # -------------------------------------------- + nx = len(FSMesh) + ny = len(FSMesh[0]) + p = np.ndarray((nx,ny, 3), dtype=np.float32) + V = np.zeros((nx,ny, 3), dtype=np.float32) + n = np.ndarray((nx,ny, 3), dtype=np.float32) + a = np.ndarray((nx,ny), dtype=np.float32) + x1 = np.zeros((nx,ny), dtype=np.float32) + x2 = np.zeros((nx,ny), dtype=np.float32) + x3 = np.zeros((nx,ny), dtype=np.float32) + dx1 = np.zeros((nx,ny), dtype=np.float32) + dx2 = np.zeros((nx,ny), dtype=np.float32) + dx3 = np.zeros((nx,ny), dtype=np.float32) + for i in range(0, nx): + for j in range(0, ny): + pos = FSMesh[i][j].pos + normal = FSMesh[i][j].normal + area = FSMesh[i][j].area + p[i,j,0] = pos.x + p[i,j,1] = pos.y + p[i,j,2] = pos.z + n[i,j,0] = normal.x + n[i,j,1] = normal.y + n[i,j,2] = normal.z + a[i,j] = area + self.fs = {'h': h, 'N':nx*ny, 'Nx':nx, 'Ny':ny, \ + 'pos':p, 'vel':V, 'normal':n, 'area':a, \ + 'x1':x1, 'x2':x2, 'x3':x3,\ + 'dx1':dx1, 'dx2':dx2, 'dx3':dx3} + # -------------------------------------------- + # Sea data (dictionary with components + # ['front','back','left','right','bottom']) + # N, Nx, Ny = Number of points in each + # direction + # pos = Positions + # vel = Velocities + # n = Normals + # area = Areas + # -------------------------------------------- + self.sea = {'ids':['front','back','left','right','bottom']} + N = 0 + for index in self.sea['ids']: + mesh = SeaMesh[index] + nx = len(mesh) + ny = len(mesh[0]) + p = np.ndarray((nx,ny, 3), dtype=np.float32) + V = np.zeros((nx,ny, 3), dtype=np.float32) + n = np.ndarray((nx,ny, 3), dtype=np.float32) + a = np.ndarray((nx,ny), dtype=np.float32) + for i in range(0, nx): + for j in range(0, ny): + pos = mesh[i][j].pos + normal = mesh[i][j].normal + area = mesh[i][j].area + p[i,j,0] = pos.x + p[i,j,1] = pos.y + p[i,j,2] = pos.z + n[i,j,0] = normal.x + n[i,j,1] = normal.y + n[i,j,2] = normal.z + a[i,j] = area + d = {'N':nx*ny, 'Nx':nx, 'Ny':ny, 'pos':p, 'vel':V, 'normal':n, 'area':a} + self.sea[index] = d + N = N + nx*ny + self.sea['N'] = N + self.sea['h'] = h + # -------------------------------------------- + # Body data + # N, Nx, Ny = Number of points in each + # direction + # pos = Positions + # vel = Velocities + # n = Normals + # area = Areas + # -------------------------------------------- + self.b = {'N':0, 'pos':None, 'vel':None, 'normal':None, 'area':None} + # -------------------------------------------- + # Waves data + # N = Number of waves + # data = Waves data + # -------------------------------------------- + nW = len(waves) + w = np.ndarray((nW, 4), dtype=np.float32) + for i in range(0,nW): + w[i,0] = waves[i][0] + w[i,1] = waves[i][1] + w[i,2] = waves[i][2] + w[i,3] = waves[i][3] + self.waves = {'h':h, 'N':nW, 'data':w} + # -------------------------------------------- + # BEM data + # N = nFS + nSea + nB + # A,B,dB = Linear system matrix and vectors + # p1,... = Velocity potentials (phi) for + # each RK4 step. In reallity are + # the independent term of the + # BEM linear system, so is the + # potential for the free surface, + # and the gradient projected over + # the normal along all other terms. + # dp1,... = Acceleration potentials + # (dphi/dt) for each RK4 step. + # In reallity are the + # independent term of the BEM + # linear system, so is the + # potential for the free surface, + # and the gradient projected over + # the normal along all other terms. + # Ap,Adp = BEM solution vectors, that + # contains the potential gradients + # on free surface, and the potential + # along all toher surfaces. + # -------------------------------------------- + nFS = self.fs['N'] + nSea = self.sea['N'] + nB = self.b['N'] + N = nFS + nSea + nB + A = np.zeros((N, N), dtype=np.float32) + B = np.zeros((N), dtype=np.float32) + dB = np.zeros((N), dtype=np.float32) + p1 = np.zeros((N), dtype=np.float32) + p2 = np.zeros((N), dtype=np.float32) + p3 = np.zeros((N), dtype=np.float32) + p4 = np.zeros((N), dtype=np.float32) + Ap = np.zeros((N), dtype=np.float32) + dp1 = np.zeros((N), dtype=np.float32) + dp2 = np.zeros((N), dtype=np.float32) + dp3 = np.zeros((N), dtype=np.float32) + dp4 = np.zeros((N), dtype=np.float32) + Adp = np.zeros((N), dtype=np.float32) + self.bem = {'N':N, 'A':A, 'B':B, 'dB':dB, \ + 'p1':p1, 'p2':p2, 'p3':p3, 'p4':p4, 'Ap':Ap, \ + 'dp1':dp1, 'dp2':dp2, 'dp3':dp3, 'dp4':dp4, 'Adp':Adp } + + def execute(self): + """ Compute initial conditions. """ + # -------------------------------------------- + # Free surface beach nodes. + # Beach nodes are the nodes of the free + # surface where the waves are imposed. All + # the other nodes are computed allowing non + # linear waves due to the ship interaction. + # The beach will have enough dimension to + # control at least half wave length + # -------------------------------------------- + # Get maximum wave length + wl = 0.0 + for w in self.waves['data']: + T = w[1] + wl = max(wl, 0.5 * grav / np.pi * T*T) + # Get nodes dimensions + nx = self.fs['Nx'] + ny = self.fs['Ny'] + lx = self.fs['pos'][nx-1,0][0] - self.fs['pos'][0,0][0] + ly = self.fs['pos'][0,ny-1][1] - self.fs['pos'][0,0][1] + dx = lx / nx + dy = ly / ny + # Get number of nodes involved + wnx = max(1, int(round(0.5*wl / dx))) + wny = max(1, int(round(0.5*wl / dy))) + wnx = min(wnx, nx) + wny = min(wny, ny) + self.fs['Beachx'] = wnx + self.fs['Beachy'] = wny + # -------------------------------------------- + # Free surface initial condition. + # Since RK4 scheme starts on the end of + # previous step, we only write on last + # stage value (p4 and dp4) + # -------------------------------------------- + nx = self.fs['Nx'] + ny = self.fs['Ny'] + h = self.fs['h'] + for i in range(0,nx): + for j in range(0,ny): + # Since initial values of the potencial, and this acceleration, + # depends on z, we need to compute first the positions. + self.fs['pos'][i,j][2] = 0. + for w in self.waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = self.fs['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + # hfact = np.sinh(k*(pos[2]+h)) / np.cosh(k*h) + hfact = 1.0 + amp = A*np.sin(k*l + phase)*hfact + self.fs['pos'][i,j][2] = self.fs['pos'][i,j][2] + amp + amp = - A*frec*np.cos(k*l + phase)*hfact + self.fs['vel'][i,j][2] = self.fs['vel'][i,j][2] + amp + # And now we can compute potentials. + for w in self.waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = self.fs['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h) + amp = - grav/frec*A*np.cos(k*l + phase)*hfact + self.bem['p4'][i*ny+j] = self.bem['p4'][i*ny+j] + amp + amp = - grav*A*np.sin(k*l + phase)*hfact + self.bem['dp4'][i*ny+j] = self.bem['dp4'][i*ny+j] + amp + # -------------------------------------------- + # Sea initial condition on sides. + # 1. Since RK4 scheme starts on the end of + # previous step, we only write on last + # stage value (p4 and dp4) + # 2. In the sea boundaries we are + # interested on the gradient of the + # potentials projected over the normal, + # so we really store this value. + # 3. In the floor this value is ever null. + # -------------------------------------------- + ids = ['front','back','left','right','bottom'] + i0 = self.fs['N'] + for index in ids: + sea = self.sea[index] + nx = sea['Nx'] + ny = sea['Ny'] + for i in range(0,nx): + for j in range(0,ny): + for w in self.waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = sea['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + normal = sea['normal'][i,j] + hfact = np.cosh(k*(pos[2]+h)) / np.cosh(k*h) + factor = np.dot(normal,np.array([np.cos(heading), np.sin(heading), 0.])) + amp = frec*A*np.sin(k*l + phase)*hfact + self.bem['p4'][i0 + i*ny+j] = self.bem['p4'][i*ny+j] + factor*amp + amp = - grav*A*k*np.cos(k*l + phase)*hfact + self.bem['dp4'][i0 + i*ny+j] = self.bem['dp4'][i*ny+j] + factor*amp + i0 = i0 + sea['N'] - def execute(self): - """ Compute initial conditions. """ - nx = self.fs['Nx'] - ny = self.fs['Ny'] - for i in range(0,nx): - for j in range(0,ny): - self.fs['pos'][i,j][2] = 0. - for w in self.waves['data']: - A = w[0] - T = w[1] - phase = w[2] - heading = np.pi*w[3]/180.0 - wl = 0.5 * grav / np.pi * T*T - k = 2.0*np.pi/wl - frec = 2.0*np.pi/T - pos = self.fs['pos'][i,j] - l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) - amp = A*np.sin(k*l + phase) - self.fs['pos'][i,j][2] = self.fs['pos'][i,j][2] + amp - amp = - grav/frec*A*np.sin(k*l + phase) - self.fs['velPot'][i,j] = self.fs['velPot'][i,j] + amp - amp = grav*A*np.cos(k*l + phase) - self.fs['accPot'][i,j] = self.fs['accPot'][i,j] + amp diff --git a/src/Mod/Ship/simRun/Sim/matrixGen.py b/src/Mod/Ship/simRun/Sim/matrixGen.py index 4eab537bc..43ea6ed51 100644 --- a/src/Mod/Ship/simRun/Sim/matrixGen.py +++ b/src/Mod/Ship/simRun/Sim/matrixGen.py @@ -1,24 +1,24 @@ #*************************************************************************** -#* * -#* Copyright (c) 2011, 2012 * -#* Jose Luis Cercos Pita * -#* * +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * +#* USA * +#* * #*************************************************************************** # numpy @@ -27,53 +27,197 @@ import numpy as np grav=9.81 class simMatrixGen: - def __init__(self, context=None, queue=None): - """ Constructor. - @param context OpenCL context where apply. Only for compatibility, - must be None. - @param queue OpenCL command queue. Only for compatibility, - must be None. - """ - self.context = context - self.queue = queue + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue - def execute(self, fs, A): - """ Compute system matrix. - @param fs Free surface instance. - @param A Linear system matrix. - """ - self.fs = fs - nx = self.fs['Nx'] - ny = self.fs['Ny'] - nF = nx*ny - nB = 0 # No body for the moment - N = nx*ny + nB - # Fluid sources rows - for i in range(0,nx): - for j in range(0,ny): - # Append fluid effect - pos = self.fs['pos'][i,j] - A[i*ny+j,0:nF] = self.fluidEffect(pos) - # Append body effect - # ... + def execute(self, x, p, dp, fs, sea, bem, body): + """ Compute system matrix. + @param x Free surface z coordinates. + @param fs Free surface instance. + @param sea Sea boundary instance. + @param bem Boundary Element Method instance. + @param body Body instance. + """ + nFS = fs['N'] + nSea = sea['N'] + nB = body['N'] + n = nFS + nSea + nB + A = bem['A'] + B = bem['B'] + dB = bem['dB'] + # Free surface sources rows + nx = fs['Nx'] + ny = fs['Ny'] + for i in range(0,nx): + for j in range(0,ny): + pos = np.copy(fs['pos'][i,j]) + pos[2] = x[i,j] + # Compute G terms + fsG = self.fsG(x, pos, fs) + seaG = self.seaG(pos, sea) + # Compute H terms + fsH = self.fsH(i*ny+j, x, pos, fs) + seaH = self.seaH(i*ny+j, pos, fs, sea) + # Append terms to linear system matrix + A[i*ny+j,0:nFS] = fsG + A[i*ny+j,nFS:n] = seaH + # Set independent terms + B[i*ny+j] = np.dot(fsH, p[0:nFS]) + np.dot(seaG, p[nFS:nFS+nSea]) + dB[i*ny+j] = np.dot(fsH, dp[0:nFS]) + np.dot(seaG, dp[nFS:nFS+nSea]) + # Append body effect + # ... + # Sea sources rows + ids = ['front','back','left','right','bottom'] + count = 0 + for index in ids: + s = sea[index] + nx = s['Nx'] + ny = s['Ny'] + for i in range(0,nx): + for j in range(0,ny): + pos = np.copy(s['pos'][i,j]) + # Compute G terms + fsG = self.fsG(x, pos, fs) + seaG = self.seaG(pos, sea) + # Compute H terms + fsH = self.fsH(nFS+count, x, pos, fs) + seaH = self.seaH(nFS+count, pos, fs, sea) + # Append terms to linear system matrix + A[nFS+count, 0:nFS] = fsG + A[nFS+count, nFS:n] = seaH + # Set independent terms + B[nFS+count] = np.dot(fsH, p[0:nFS]) + np.dot(seaG, p[nFS:nFS+nSea]) + dB[nFS+count] = np.dot(fsH, dp[0:nFS]) + np.dot(seaG, dp[nFS:nFS+nSea]) + # Append body effect + # ... + count = count + 1 + # Solid sources rows + # ... + + def fsG(self, x, pos, fs): + r""" Compute free surface terms potential effect over desired position. Desingularized + sources must taken into account. + \$ G_{ij} = \sum_{j=0}^{n_{FS}-1} \log(\mathbf{r}_{ij}) \$ + @param x Free surface z coordinates. + @param pos Point to evaluate. + @param fs Free surface instance. + @return Free surface effect row. + """ + nx = fs['Nx'] + ny = fs['Ny'] + nF = nx*ny + row = np.ndarray(nF, dtype=np.float32) + for i in range(0,nx): + for j in range(0,ny): + # Get source position (desingularized) + source = np.copy(fs['pos'][i,j]) + source[2] = x[i,j] + area = fs['area'][i,j] + normal = fs['normal'][i,j] + source = source + np.sqrt(area)*normal + # Get union vector between points + r = pos-source + row[i*ny+j] = area * 0.5*np.log(np.dot(r,r)) + return row + + def fsH(self, index, x, pos, fs): + r""" Compute free surface terms potential gradient effect over desired position. Desingularized + sources must taken into account. + \$ H_{ij} = \sum_{j=0}^{n_{FS}-1} \frac{\mathbf{r}_{ij}}{\vert \mathbf{r}_{ij} \vert^2} \$ + When the point effect over himself is considered, -1/2 must be append. + @param index Potential point index. + @param x Free surface z coordinates. + @param pos Point to evaluate. + @param fs Free surface instance. + @return Free surface effect row. + """ + nx = fs['Nx'] + ny = fs['Ny'] + nF = nx*ny + row = np.ndarray(nF, dtype=np.float32) + for i in range(0,nx): + for j in range(0,ny): + # Get source position (desingularized) + source = np.copy(fs['pos'][i,j]) + source[2] = x[i,j] + area = fs['area'][i,j] + normal = fs['normal'][i,j] + source = source + np.sqrt(area)*normal + # Get union vector between points + r = pos-source + row[i*ny+j] = area * np.dot(r,normal) / np.dot(r,r) + # If effect over himslef is considered, apply the correction + if(index == i*ny+j): + row[i*ny+j] = row[i*ny+j] - 0.5 + return row + + def seaG(self, pos, sea): + r""" Compute sea boundary terms potential effect over desired position. Desingularized + sources must taken into account. + \$ G_{ij} = \sum_{j=0}^{n_{FS}-1} \log(\mathbf{r}_{ij}) \$ + @param pos Point to evaluate. + @param sea Sea boundaries instance. + @return Sea boundaries effect row. + """ + ids = ['front','back','left','right','bottom'] + count = 0 + row = np.ndarray(sea['N'], dtype=np.float32) + for index in ids: + s = sea[index] + nx = s['Nx'] + ny = s['Ny'] + for i in range(0,nx): + for j in range(0,ny): + # Get source position (desingularized) + source = np.copy(s['pos'][i,j]) + area = s['area'][i,j] + normal = s['normal'][i,j] + source = source + np.sqrt(area)*normal + # Get distance between points + r = pos-source + row[count] = area * 0.5*np.log(np.dot(r,r)) + count = count + 1 + return row + + def seaH(self, index, pos, fs, sea): + r""" Compute sea boundary terms potential gradient effect over desired position. Desingularized + sources must taken into account. + \$ H_{ij} = \sum_{j=0}^{n_{FS}-1} \frac{\mathbf{r}_{ij}}{\vert \mathbf{r}_{ij} \vert^2} \$ + When the point effect over himself is considered, -1/2 must be append. + @param index Potential point index. + @param pos Point to evaluate. + @param fs Free surface instance. + @param sea Sea boundaries instance. + @return Sea boundaries effect row. + """ + nF = fs['N'] + ids = ['front','back','left','right','bottom'] + count = 0 + row = np.ndarray(sea['N'], dtype=np.float32) + for index in ids: + s = sea[index] + nx = s['Nx'] + ny = s['Ny'] + for i in range(0,nx): + for j in range(0,ny): + # Get source position (desingularized) + source = np.copy(s['pos'][i,j]) + area = s['area'][i,j] + normal = s['normal'][i,j] + source = source + np.sqrt(area)*normal + # Get distance between points + r = pos-source + row[count] = area * np.dot(r,normal) / np.dot(r,r) + # If effect over himslef is considered, apply the correction + if(index == count+nF): + row[count] = row[count] - 0.5 + count = count + 1 + return row - def fluidEffect(self, pos): - """ Compute fluid effect terms over desired position. Desingularized - sources must taken into account. - @param pos Point to evaluate. - @return Fluid effect row. - """ - nx = self.fs['Nx'] - ny = self.fs['Ny'] - nF = nx*ny - row = np.ndarray(nF, dtype=np.float32) - for i in range(0,nx): - for j in range(0,ny): - # Get source position (desingularized) - source = np.copy(self.fs['pos'][i,j]) - area = self.fs['area'][i,j] - source[2] = source[2] + np.sqrt(area) - # Get distance between points - d = np.linalg.norm(pos-source) - row[i*ny+j] = np.log(d)*area - return row \ No newline at end of file diff --git a/src/Mod/Ship/simRun/Simulation.py b/src/Mod/Ship/simRun/Simulation.py index 06773fc4e..713d196d0 100644 --- a/src/Mod/Ship/simRun/Simulation.py +++ b/src/Mod/Ship/simRun/Simulation.py @@ -51,14 +51,17 @@ class Singleton(type): class FreeCADShipSimulation(threading.Thread): __metaclass__ = Singleton - def __init__ (self, device, endTime, output, simInstance, FSmesh, waves): + def __init__ (self, device, endTime, output, simInstance, FSMesh, FSData, waves, Sea_Nx, Sea_Ny): """ Thread constructor. @param device Device to use. @param endTime Maximum simulation time. @param output [Rate,Type] Output rate, Type=0 if FPS, 1 if IPF. @param simInstance Simulaation instance. - @param FSmesh Free surface mesh faces. + @param FSMesh Free surface mesh faces. + @param FSData Free surface data (Length, Breath, Nx, Ny). @param waves Waves parameters (A,T,phi,heading) + @param Sea_Nx Times that the free surface is virtually repeated in the x direction + @param Sea_Ny Times that the free surface is virtually repeated in the y direction """ threading.Thread.__init__(self) # Setup as stopped @@ -75,9 +78,11 @@ class FreeCADShipSimulation(threading.Thread): self.endTime = endTime self.output = output self.sim = simInstance - self.FSmesh = FSmesh + self.FSMesh = FSMesh + self.FSData = FSData self.waves = waves - + self.Sea = (Sea_Nx, Sea_Ny) + def run(self): """ Runs the simulation. """ @@ -90,36 +95,116 @@ class FreeCADShipSimulation(threading.Thread): msg = QtGui.QApplication.translate("ship_console","Initializating", None,QtGui.QApplication.UnicodeUTF8) FreeCAD.Console.PrintMessage("\t[Sim]: " + msg + "...\n") - init = simInitialization(self.FSmesh,self.waves,self.context,self.queue) - matGen = simMatrixGen(self.context,self.queue) - solver = simComputeSources(self.context,self.queue) - fsEvol = simFSEvolution(self.context,self.queue) - A = init.A - FS = init.fs - waves = init.waves - dt = init.dt + if self.device == None: # Can't use OpenCL + init = simInitialization(self.FSMesh,self.FSData,self.waves,self.Sea,self.context,self.queue) + matGen = simMatrixGen(self.context,self.queue) + solver = simBEMSolver(self.context,self.queue) + evol = simEvolution(self.context,self.queue) + else: + init = simInitialization_cl(self.FSMesh,self.FSData,self.waves,self.Sea,self.context,self.queue) + matGen = simMatrixGen_cl(self.context,self.queue) + solver = simBEMSolver_cl(self.context,self.queue) + evol = simEvolution_cl(self.context,self.queue) + FS = init.fs + sea = init.sea + body = init.b + waves = init.waves + BEM = init.bem + dt = init.dt self.t = 0.0 self.FS = FS - nx = FS['Nx'] - ny = FS['Ny'] + nx = FS['Nx'] + ny = FS['Ny'] msg = QtGui.QApplication.translate("ship_console","Iterating", None,QtGui.QApplication.UnicodeUTF8) FreeCAD.Console.PrintMessage("\t[Sim]: " + msg + "...\n") while self.active and self.t < self.endTime: - msg = QtGui.QApplication.translate("ship_console","Generating linear system matrix", - None,QtGui.QApplication.UnicodeUTF8) - FreeCAD.Console.PrintMessage("\t\t[Sim]: " + msg + "...\n") - matGen.execute(FS, A) - msg = QtGui.QApplication.translate("ship_console","Solving linear systems", - None,QtGui.QApplication.UnicodeUTF8) - FreeCAD.Console.PrintMessage("\t\t[Sim]: " + msg + "...\n") - solver.execute(FS, A) - msg = QtGui.QApplication.translate("ship_console","Time integrating", - None,QtGui.QApplication.UnicodeUTF8) - FreeCAD.Console.PrintMessage("\t\t[Sim]: " + msg + "...\n") - fsEvol.execute(FS, waves, dt, self.t) + # Simple Euler method + FreeCAD.Console.PrintMessage("\t\t\t[Sim]: Generating matrix...\n") + matGen.execute(FS, waves, sea, BEM, body, self.t) + FreeCAD.Console.PrintMessage("\t\t\t[Sim]: Solving linear system matrix...\n") + solver.execute(BEM) + FreeCAD.Console.PrintMessage("\t\t\t[Sim]: Integrating...\n") + # evol.execute(FS, BEM, waves, self.t, dt) + + + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- + evol.BC(FS, BEM, waves, self.t) + f = open("%.4f.dat" % (self.t), 'w') + for i in range(0,FS['Nx']): + for j in range(0, FS['Ny']): + # Compute analitical solution + z = 0.0 + vz = 0.0 + p = 0.0 + vp = 0.0 + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = FS['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = A*np.sin(k*l - frec*self.t + phase) + z = z + amp + amp = - A*frec*np.cos(k*l - frec*self.t + phase) + vz = vz + amp + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = FS['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*frec/k*np.cos(k*l - frec*self.t + phase)*np.exp(k*z) + p = p + amp + amp = - A*9.81*np.sin(k*l - frec*self.t + phase)*np.exp(k*z) + vp = vp + amp + + """ + z = 0. + vz = 0. + p = 0. + vp = 0. + # We can do phi = Green's function + dx = FS['pos'][i,j][0] + dy = FS['pos'][i,j][1] + dz = 15.0 # An arbitrary value > 0 + p = 1. / (4. * np.pi * np.sqrt(dx*dx + dy*dy + dz*dz)) + vz = - dz / (4. * np.pi * (dx*dx + dy*dy + dz*dz)**(1.5)) + """ + + # write coordinates + f.write("%g %g " % (FS['pos'][i,j,0],FS['pos'][i,j,1])) + # write computed wave and velocity + f.write("%g %g " % (FS['pos'][i,j,2],FS['vel'][i,j,2])) + # write computed potential and time variation rate + # f.write("%g %g " % (BEM['p'][i*FS['Ny']+j],BEM['dpdt'][i*FS['Ny']+j])) + f.write("%g %g " % (BEM['p'][i*FS['Ny']+j],0.)) + # write analytic wave and velocity + f.write("%g %g " % (z,vz)) + # write analytic potential and time variation rate + # f.write("%g %g\n" % (p,vp)) + f.write("%g %g\n" % (p,0.)) + f.write("\n") + f.close() + evol.Integrate(FS, BEM, waves, self.t, dt) + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- + + self.t = self.t + dt FreeCAD.Console.PrintMessage('\t[Sim]: t = %g s\n' % (self.t)) + # Set thread as stopped (and prepare it to restarting) self.active = False threading.Event().set() diff --git a/src/Mod/Ship/simRun/TaskPanel.py b/src/Mod/Ship/simRun/TaskPanel.py index 628347458..bf9996484 100644 --- a/src/Mod/Ship/simRun/TaskPanel.py +++ b/src/Mod/Ship/simRun/TaskPanel.py @@ -61,18 +61,22 @@ class TaskPanel: device = d count = count + 1 # Get free surfaces data - FSMesh = SimInstance.FSMesh(self.sim) - wData = self.sim.Waves - wDir = self.sim.Waves_Dir - waves = [] + FSMesh = SimInstance.FSMesh(self.sim) + FSData = (self.sim.L,self.sim.B,self.sim.FS_Nx,self.sim.FS_Ny) + wData = self.sim.Waves + wDir = self.sim.Waves_Dir + waves = [] for i in range(0,len(wData)): waves.append([wData[i].x, wData[i].y, wData[i].z, wDir[i]]) + SeaNx = self.sim.Sea_Nx + SeaNy = self.sim.Sea_Ny msg = QtGui.QApplication.translate("ship_console","Launching simulation", None,QtGui.QApplication.UnicodeUTF8) App.Console.PrintMessage(msg + "...\n") # Build simulation thread - simulator = Sim(device, endTime, output, self.sim, FSMesh, waves) - simulator.start() + simulator = Sim(device, endTime, output, self.sim, FSMesh, FSData, waves, SeaNx, SeaNy) + simulator.start() # Activate me for final release + # simulator.run() # Activate me for development (i will show python fails) msg = QtGui.QApplication.translate("ship_console","Done", None,QtGui.QApplication.UnicodeUTF8) App.Console.PrintMessage(msg + "!\n") diff --git a/src/Mod/Ship/simRun/clSim/BEMsolver.py b/src/Mod/Ship/simRun/clSim/BEMsolver.py new file mode 100644 index 000000000..42e54a89a --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/BEMsolver.py @@ -0,0 +1,61 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np +from bem_jacobi_cl import jacobi +from bem_minres_cl import minres +from bem_lsqr_cl import lsqr +import FreeCAD + +grav=9.81 + +class simBEMSolver_cl: + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. + @param queue OpenCL command queue. + """ + self.context = context + self.queue = queue + self.solver = lsqr(context, queue) + + def execute(self, bem): + """ Compute potential unknow data (gradients for free surface, and + potentials for the other ones). + @param bem Boundary Element Method instance. + """ + """ + [bem['gradp'], r, iters] = self.solver.solve(bem['A'], bem['B'], bem['gradp']) + if(iters >= 300): + FreeCAD.Console.PrintError("\t\t[Sim]: Solving velocity potentials.\n") + FreeCAD.Console.PrintError("\t\t\tSolutions seems don't convergs after 300 iterations (%g residual)\n" % (r)) + FreeCAD.Console.PrintMessage((r,iters)) + FreeCAD.Console.PrintMessage("\n") + """ + import scipy.linalg as la + [bem['gradp'], residues, rank, s] = la.lstsq(bem['A'], bem['B']) + if(rank < bem['N']): + FreeCAD.Console.PrintError("\t\t[Sim]: Solving velocity potentials.\n") + FreeCAD.Console.PrintError("\t\t\tEffective rank of linear system matrix is {0} (N = {1})\n".format(rank, bem['N'])) + diff --git a/src/Mod/Ship/simRun/clSim/__init__.py b/src/Mod/Ship/simRun/clSim/__init__.py index 2fcb8e495..6a185fde4 100644 --- a/src/Mod/Ship/simRun/clSim/__init__.py +++ b/src/Mod/Ship/simRun/clSim/__init__.py @@ -21,4 +21,7 @@ #* * #*************************************************************************** -import initialization, Utils \ No newline at end of file +from initialization import * +from matrixGen import * +from BEMsolver import * +from evolution import * diff --git a/src/Mod/Ship/simRun/clSim/bem_jacobi_cl.py b/src/Mod/Ship/simRun/clSim/bem_jacobi_cl.py new file mode 100644 index 000000000..368574f22 --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/bem_jacobi_cl.py @@ -0,0 +1,155 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np + +# pyOpenCL +import pyopencl as cl +from pyopencl.reduction import ReductionKernel +import pyopencl.array as cl_array +import clUtils + +import FreeCAD +grav=9.81 + +class jacobi: + def __init__(self, context, queue): + """ Constructor. + @param context OpenCL context where apply. + @param queue OpenCL command queue. + """ + self.context = context + self.queue = queue + self.program = clUtils.loadProgram(context, clUtils.path() + "/jacobi.cl") + # Create OpenCL objects as null objects, that we will generate + # at the first iteration + self.A = None + self.B = None + self.X0 = None + self.X = None + self.x = None + # Create dot operator + self.dot = ReductionKernel(context, np.float32, neutral="0", + reduce_expr="a+b", map_expr="x[i]*y[i]", + arguments="__global float *x, __global float *y") + + def solve(self, A, B, x0=None, tol=10e-6, iters=300, w=1.0): + r""" Solve linear system of equations by a Jacobi + iterative method. + @param A Linear system matrix. + @param B Linear system independent term. + @param x0 Initial aproximation of the solution. + @param tol Relative error tolerance: \n + \$ \vert\vert B - A \, x \vert \vert_\infty / + \vert\vert B \vert \vert_\infty \$ + @param iters Maximum number of iterations. + @param w Relaxation factor + """ + # Create/set OpenCL buffers + w = np.float32(w) + self.setBuffers(A,B,x0) + # Get dimensions for OpenCL execution + n = np.uint32(len(B)) + gSize = (clUtils.globalSize(n),) + # Get a norm to can compare later for valid result + B_cl = cl_array.to_device(self.context,self.queue,B) + bnorm2 = self.dot(B_cl,B_cl).get() + w = w / bnorm2 + FreeCAD.Console.PrintMessage(bnorm2) + FreeCAD.Console.PrintMessage("\n") + rnorm2 = 0. + # Iterate while the result converges or maximum number + # of iterations is reached. + for i in range(0,iters): + kernelargs = (self.A, + self.B, + self.X0, + self.X, + n) + # Test if the final result has been reached + self.program.r(self.queue, gSize, None, *(kernelargs)) + cl.enqueue_read_buffer(self.queue, self.X, self.x).wait() + x_cl = cl_array.to_device(self.context,self.queue,self.x) + rnorm2 = self.dot(x_cl,x_cl).get() + FreeCAD.Console.PrintMessage("\t") + FreeCAD.Console.PrintMessage(rnorm2) + FreeCAD.Console.PrintMessage("\n") + if np.sqrt(rnorm2 / bnorm2) <= tol: + break + # Iterate + kernelargs = (self.A, + self.B, + self.X0, + self.X, + w, + n) + self.program.jacobi(self.queue, gSize, None, *(kernelargs)) + kernelargs = (self.A, + self.B, + self.X, + self.X0, + w, + n) + self.program.jacobi(self.queue, gSize, None, *(kernelargs)) + # Return result computed + cl.enqueue_read_buffer(self.queue, self.X0, self.x).wait() + return (np.copy(self.x), np.sqrt(rnorm2 / bnorm2), i) + + def setBuffers(self, A,B,x0): + """ Create/set OpenCL required buffers. + @param A Linear system matrix. + @param B Independent linear term. + @param x0 Initial solution estimator. + """ + # Get dimensions + shape = np.shape(A) + if len(shape) != 2: + raise ValueError, 'Matrix A must be 2 dimensional array' + if shape[0] != shape[1]: + raise ValueError, 'Square linear system matrix expected' + if len(B) != shape[0]: + raise ValueError, 'Matrix and independet term dimensions does not match' + n = len(B) + # Set x0 if not provided + if x0 != None: + if len(x0) != n: + raise ValueError, 'Initial solution estimator length does not match with linear system dimensions' + if x0 == None: + x0 = B + # Create OpenCL objects if not already generated + if not self.A: + mf = cl.mem_flags + self.A = cl.Buffer( self.context, mf.READ_ONLY, size = n*n * np.dtype('float32').itemsize ) + self.B = cl.Buffer( self.context, mf.READ_ONLY, size = n * np.dtype('float32').itemsize ) + self.X0 = cl.Buffer( self.context, mf.READ_WRITE, size = n * np.dtype('float32').itemsize ) + self.X = cl.Buffer( self.context, mf.READ_WRITE, size = n * np.dtype('float32').itemsize ) + self.x = np.zeros((n), dtype=np.float32) + # Transfer data to buffers + events = [] + events.append(cl.enqueue_write_buffer(self.queue, self.A, A.reshape((n*n)) )) + events.append(cl.enqueue_write_buffer(self.queue, self.B, B)) + events.append(cl.enqueue_write_buffer(self.queue, self.X0, x0)) + for e in events: + e.wait() + diff --git a/src/Mod/Ship/simRun/clSim/bem_lsqr_cl.py b/src/Mod/Ship/simRun/clSim/bem_lsqr_cl.py new file mode 100644 index 000000000..39811a390 --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/bem_lsqr_cl.py @@ -0,0 +1,229 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np + +# pyOpenCL +import pyopencl as cl +from pyopencl.reduction import ReductionKernel +from pyopencl.elementwise import ElementwiseKernel +import pyopencl.array as cl_array +import clUtils + +import FreeCAD +grav=9.81 + +class lsqr: + def __init__(self, context, queue): + """ Constructor. + @param context OpenCL context where apply. + @param queue OpenCL command queue. + """ + self.context = context + self.queue = queue + self.program = clUtils.loadProgram(context, clUtils.path() + "/lsqr.cl") + # Create OpenCL objects as null objects, that we will generate + # at the first iteration + self.A = None + self.B = None + self.X0 = None + self.X = None + self.R = None + # Create dot operator + self.dot = ReductionKernel(context, np.float32, neutral="0", + reduce_expr="a+b", map_expr="x[i]*y[i]", + arguments="__global float *x, __global float *y") + self.dot_c_vec = ElementwiseKernel(context, + "float c, float *v", + "v[i] *= c") + self.copy_vec = ElementwiseKernel(context, + "float* out, float *in", + "out[i] = in[i]") + self.linear_comb = ElementwiseKernel(context, + "float* z," + "float a, float *x, " + "float b, float *y", + "z[i] = a*x[i] + b*y[i]") + self.prod = ElementwiseKernel(context, + "float* z," + "float *x, float *y", + "z[i] = x[i]*y[i]") + + def solve(self, A, B, x0=None, tol=10e-6, iters=300): + r""" Solve linear system of equations by a Jacobi + iterative method. + @param A Linear system matrix. + @param B Linear system independent term. + @param x0 Initial aproximation of the solution. + @param tol Relative error tolerance: \n + \$ \vert\vert B - A \, x \vert \vert_\infty / + \vert\vert B \vert \vert_\infty \$ + @param iters Maximum number of iterations. + """ + # Create/set OpenCL buffers + self.setBuffers(A,B,x0) + # Get dimensions for OpenCL execution + n = np.uint32(len(B)) + gSize = (clUtils.globalSize(n),) + # Preconditionate matrix + self.precondition(n) + # Get a norm to can compare later for valid result + bnorm = np.sqrt(self.dot(self.b,self.b).get()) + FreeCAD.Console.PrintMessage(bnorm) + FreeCAD.Console.PrintMessage("\n") + # Initialize the problem + beta = bnorm + self.dot_c_vec(1.0/beta, self.u) + kernelargs = (self.A,self.u.data,self.v.data,n) + self.program.dot_matT_vec(self.queue, gSize, None, *(kernelargs)) + alpha = np.sqrt(self.dot(self.v,self.v).get()) + self.dot_c_vec(1.0/alpha, self.v) + self.copy_vec(self.w, self.v) + rhobar = alpha + phibar = beta + # Iterate while the result converges or maximum number + # of iterations is reached. + for i in range(0,iters): + # Compute residues + kernelargs = (self.A, + self.b.data, + self.x.data, + self.r.data, + n) + self.program.r(self.queue, gSize, None, *(kernelargs)) + rnorm = np.sqrt(self.dot(self.r,self.r).get()) + FreeCAD.Console.PrintMessage("\t") + FreeCAD.Console.PrintMessage(rnorm) + FreeCAD.Console.PrintMessage("\n") + # Test if the final result has been reached + if rnorm / bnorm <= tol: + break + # Compute next alpha, beta, u, v + kernelargs = (self.A,self.u.data,self.v.data,self.u.data,alpha,n) + self.program.u(self.queue, gSize, None, *(kernelargs)) + beta = np.sqrt(self.dot(self.u,self.u).get()) + FreeCAD.Console.PrintMessage("\t beta=") + FreeCAD.Console.PrintMessage(beta) + FreeCAD.Console.PrintMessage("\n") + self.dot_c_vec(1.0/beta, self.u) + kernelargs = (self.A,self.u.data,self.v.data,self.v.data,beta,n) + self.program.v(self.queue, gSize, None, *(kernelargs)) + alpha = np.sqrt(self.dot(self.v,self.v).get()) + FreeCAD.Console.PrintMessage("\t alpha=") + FreeCAD.Console.PrintMessage(alpha) + FreeCAD.Console.PrintMessage("\n") + self.dot_c_vec(1.0/alpha, self.v) + # Apply the orthogonal transformation + rho = np.sqrt(rhobar*rhobar + beta*beta) + c = rhobar/rho + s = beta*rho + theta = s*alpha + rhobar = -c*alpha + phi = c*phibar + phibar = s*phibar + # Update x and w + self.linear_comb(self.x, 1, self.x, phi/rho, self.w) + self.linear_comb(self.w, 1, self.v, theta/rho, self.w) + # Correct returned result due to the precoditioning + self.prod(self.x, self.xf, self.x) + # Return result computed + x = np.zeros((n), dtype=np.float32) + cl.enqueue_read_buffer(self.queue, self.x.data, x).wait() + return (x, rnorm / bnorm, i) + + def setBuffers(self, A,B,x0): + """ Create/set OpenCL required buffers. + @param A Linear system matrix. + @param B Independent linear term. + @param x0 Initial solution estimator. + """ + # Get dimensions + shape = np.shape(A) + if len(shape) != 2: + raise ValueError, 'Matrix A must be 2 dimensional array' + if shape[0] != shape[1]: + raise ValueError, 'Square linear system matrix expected' + if len(B) != shape[0]: + raise ValueError, 'Matrix and independet term dimensions does not match' + n = len(B) + # Set x0 if not provided + if x0 != None: + if len(x0) != n: + raise ValueError, 'Initial solution estimator length does not match with linear system dimensions' + if x0 == None: + x0 = B + # Create OpenCL objects if not already generated + if not self.A: + mf = cl.mem_flags + self.A = cl.Buffer( self.context, mf.READ_WRITE, size = n*n * np.dtype('float32').itemsize ) + self.b = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.x = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.xf = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.r = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.u = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.v = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.w = cl_array.zeros(self.context,self.queue, (n), np.float32) + # Transfer data to buffers + events = [] + events.append(cl.enqueue_write_buffer(self.queue, self.A, A.reshape((n*n)) )) + self.b.set(B) + self.x.set(x0) + self.u.set(B) + for e in events: + e.wait() + + def precondition(self, n): + """ Preconditionate matrix, ensuring that all linear system + matrix columns has an acceptable norm. Of course, final + solution vector must be corrected conveniently. + @param n Linear system dimension. + """ + gSize = (clUtils.globalSize(n),) + xf = np.ones((n), dtype=np.float32) + for i in range(0,n): + col = np.uint32(i) + # Compute column norm + # We can use v as column vector because has not been used yet + kernelargs = (self.A, + self.v.data, + col, + n) + self.program.column(self.queue, gSize, None, *(kernelargs)) + norm = np.sqrt(self.dot(self.v,self.v).get()) + FreeCAD.Console.PrintMessage("col ") + FreeCAD.Console.PrintMessage(i) + FreeCAD.Console.PrintMessage(", norm=") + FreeCAD.Console.PrintMessage(norm) + FreeCAD.Console.PrintMessage("\n") + if norm < 1.0: + continue + fact = np.float32(1.0/norm) + xf[i] = fact + kernelargs = (self.A, + fact, + col, + n) + self.program.prod_c_column(self.queue, gSize, None, *(kernelargs)) + self.x.set(xf) + diff --git a/src/Mod/Ship/simRun/clSim/bem_minres_cl.py b/src/Mod/Ship/simRun/clSim/bem_minres_cl.py new file mode 100644 index 000000000..51e82d585 --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/bem_minres_cl.py @@ -0,0 +1,157 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np + +# pyOpenCL +import pyopencl as cl +from pyopencl.reduction import ReductionKernel +import pyopencl.array as cl_array +import clUtils + +import FreeCAD +grav=9.81 + +class minres: + def __init__(self, context, queue): + """ Constructor. + @param context OpenCL context where apply. + @param queue OpenCL command queue. + """ + self.context = context + self.queue = queue + self.program = clUtils.loadProgram(context, clUtils.path() + "/minres.cl") + # Create OpenCL objects as null objects, that we will generate + # at the first iteration + self.A = None + self.B = None + self.X0 = None + self.X = None + self.R = None + # Create dot operator + self.dot = ReductionKernel(context, np.float32, neutral="0", + reduce_expr="a+b", map_expr="x[i]*y[i]", + arguments="__global float *x, __global float *y") + + def solve(self, A, B, x0=None, tol=10e-6, iters=300): + r""" Solve linear system of equations by a Jacobi + iterative method. + @param A Linear system matrix. + @param B Linear system independent term. + @param x0 Initial aproximation of the solution. + @param tol Relative error tolerance: \n + \$ \vert\vert B - A \, x \vert \vert_\infty / + \vert\vert B \vert \vert_\infty \$ + @param iters Maximum number of iterations. + """ + # Create/set OpenCL buffers + self.setBuffers(A,B,x0) + # Get dimensions for OpenCL execution + n = np.uint32(len(B)) + gSize = (clUtils.globalSize(n),) + # Get a norm to can compare later for valid result + B_cl = cl_array.to_device(self.context,self.queue,B) + bnorm2 = self.dot(B_cl,B_cl).get() + FreeCAD.Console.PrintMessage(bnorm2) + FreeCAD.Console.PrintMessage("\n") + # Iterate while the result converges or maximum number + # of iterations is reached. + for i in range(0,iters): + # Compute residues + kernelargs = (self.A, + self.B, + self.X0, + self.R.data, + n) + # Test if the final result has been reached + self.program.r(self.queue, gSize, None, *(kernelargs)) + rnorm2 = self.dot(self.R,self.R).get() + FreeCAD.Console.PrintMessage("\t") + FreeCAD.Console.PrintMessage(rnorm2) + FreeCAD.Console.PrintMessage("\n") + if np.sqrt(rnorm2 / bnorm2) <= tol: + break + # Iterate + kernelargs = (self.A, + self.R.data, + self.AR.data, + n) + self.program.dot_mat_vec(self.queue, gSize, None, *(kernelargs)) + AR_R = self.dot(self.AR,self.R).get() + AR_AR = self.dot(self.AR,self.AR).get() + kernelargs = (self.A, + self.R.data, + self.X, + self.X0, + AR_R, + AR_AR, + n) + self.program.minres(self.queue, gSize, None, *(kernelargs)) + # Swap variables + swap = self.X + self.X = self.X0 + self.X0 = swap + # Return result computed + x = np.zeros((n), dtype=np.float32) + cl.enqueue_read_buffer(self.queue, self.X0, x).wait() + return (x, np.sqrt(rnorm2 / bnorm2), i) + + def setBuffers(self, A,B,x0): + """ Create/set OpenCL required buffers. + @param A Linear system matrix. + @param B Independent linear term. + @param x0 Initial solution estimator. + """ + # Get dimensions + shape = np.shape(A) + if len(shape) != 2: + raise ValueError, 'Matrix A must be 2 dimensional array' + if shape[0] != shape[1]: + raise ValueError, 'Square linear system matrix expected' + if len(B) != shape[0]: + raise ValueError, 'Matrix and independet term dimensions does not match' + n = len(B) + # Set x0 if not provided + if x0 != None: + if len(x0) != n: + raise ValueError, 'Initial solution estimator length does not match with linear system dimensions' + if x0 == None: + x0 = B + # Create OpenCL objects if not already generated + if not self.A: + mf = cl.mem_flags + self.A = cl.Buffer( self.context, mf.READ_ONLY, size = n*n * np.dtype('float32').itemsize ) + self.B = cl.Buffer( self.context, mf.READ_ONLY, size = n * np.dtype('float32').itemsize ) + self.X0 = cl.Buffer( self.context, mf.READ_WRITE, size = n * np.dtype('float32').itemsize ) + self.X = cl.Buffer( self.context, mf.READ_WRITE, size = n * np.dtype('float32').itemsize ) + self.R = cl_array.zeros(self.context,self.queue, (n), np.float32) + self.AR = cl_array.zeros(self.context,self.queue, (n), np.float32) + # Transfer data to buffers + events = [] + events.append(cl.enqueue_write_buffer(self.queue, self.A, A.reshape((n*n)) )) + events.append(cl.enqueue_write_buffer(self.queue, self.B, B)) + events.append(cl.enqueue_write_buffer(self.queue, self.X0, x0)) + for e in events: + e.wait() + diff --git a/src/Mod/Ship/simRun/clSim/clUtils.py b/src/Mod/Ship/simRun/clSim/clUtils.py new file mode 100644 index 000000000..676a23019 --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/clUtils.py @@ -0,0 +1,58 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# FreeCAD +from shipUtils import Paths + +# pyOpenCL +import pyopencl as cl +import numpy as np + +# Standard +import math + +def loadProgram(context, file): + """ Loads a file and comnpile it. + @param context OpenCL context where apply. + @param file File to load and compile. + @return Ready to use OpenCL program. + """ + f = open(file, 'r') + str = "".join(f.readlines()) + return cl.Program(context, str).build() + +def path(): + """ Gets the OpenCL kernels path + @return OpenCL kernels path + """ + path = Paths.modulePath() + "/resources/opencl" + return path + +def globalSize(n): + """ Compute global size from amount of data. + @param n Amount of data. + @return global size. + """ + localSize = 256.0 + return int(math.ceil(n/localSize)*localSize) + diff --git a/src/Mod/Ship/simRun/clSim/evolution.py b/src/Mod/Ship/simRun/clSim/evolution.py new file mode 100644 index 000000000..4f62ba6fc --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/evolution.py @@ -0,0 +1,258 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np +import FreeCAD + +grav=9.81 + +class simEvolution_cl: + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue + + def execute(self, fs, bem, waves, t, dt): + """ Compute the variables unknow for the next time step. + @param fs Free surface instance. + @param bem Boundary elements method instance. + @param waves Waves instance. + @param t Simulation time. + @param dt Time step. + """ + self.BC(fs,bem,waves, t) + self.Integrate(fs,bem,waves, t,dt) + + def BC(self, fs, bem, waves, t): + """ Apply the boundary conditions to compute time variation rate + of the unknow variables. + @param fs Free surface instance. + @param bem Boundary elements method instance. + @param waves Waves instance. + @param t Simulation time. + """ + nx = fs['Nx'] + ny = fs['Ny'] + nFS = nx*ny + for i in range(0,nx): + for j in range(0,ny): + gradp = np.copy(bem['gradp'][i*ny+j]) + z = fs['pos'][i,j,2] + bem['dpdt'][i*ny+j] = - 0.5 * gradp**2.0 - 9.81*z + fs['vel'][i,j,2] = gradp + # Since the inverse method returns significant errors near + # to the free surface borders, we will modify 3 area + # elements of the border such that the last one will be + # exactly the analytic solution. Also we will use it as + # numerical beach in order to disipate waves generated + # inside the domain (that will be refelceted otherwise) + # 1.- Corners + for i in range(0,4)+range(nx-4,nx): + if i in range(0,4): + fx = 1. - i/4. + else: + fx = (i - nx + 5) / 4. + for j in range(0,4)+range(ny-4,ny): + if j in range(0,4): + fy = 1. - j/4. + else: + fy = (j - ny + 5) / 4. + factor = max(fx,fy) + pos = fs['pos'][i,j] + dpdt = 0. + vel = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*9.81*np.sin(k*l - frec*t + phase)*np.exp(k*pos[2]) + dpdt = dpdt + amp + amp = - A*frec*np.cos(k*l - frec*t + phase) + vel = vel + amp + bem['dpdt'][i*ny+j] = factor*dpdt + (1.-factor)*bem['dpdt'][i*ny+j] + fs['vel'][i,j,2] = factor*vel + (1.-factor)*fs['vel'][i,j,2] + # 2.- rows + for i in range(0,4)+range(nx-4,nx): + if i in range(0,4): + factor = 1. - i/4. + else: + factor = (i - nx + 5) / 4. + for j in range(4, ny-4): + pos = fs['pos'][i,j] + dpdt = 0. + vel = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*9.81*np.sin(k*l - frec*t + phase)*np.exp(k*pos[2]) + dpdt = dpdt + amp + amp = - A*frec*np.cos(k*l - frec*t + phase) + vel = vel + amp + bem['dpdt'][i*ny+j] = factor*dpdt + (1.-factor)*bem['dpdt'][i*ny+j] + fs['vel'][i,j,2] = factor*vel + (1.-factor)*fs['vel'][i,j,2] + # 3.- columns + for j in range(0,4)+range(ny-4,ny): + if j in range(0,4): + factor = 1. - j/4. + else: + factor = (j - ny + 5) / 4. + for i in range(4, nx-4): + pos = fs['pos'][i,j] + dpdt = 0. + vel = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*9.81*np.sin(k*l - frec*t + phase)*np.exp(k*pos[2]) + dpdt = dpdt + amp + amp = - A*frec*np.cos(k*l - frec*t + phase) + vel = vel + amp + bem['dpdt'][i*ny+j] = factor*dpdt + (1.-factor)*bem['dpdt'][i*ny+j] + fs['vel'][i,j,2] = factor*vel + (1.-factor)*fs['vel'][i,j,2] + + def Integrate(self, fs, bem, waves, t, dt): + """ Perform time integration of the unknow variables. + @param fs Free surface instance. + @param bem Boundary elements method instance. + @param waves Waves instance. + @param t Simulation time. + @param dt Time step. + """ + nx = fs['Nx'] + ny = fs['Ny'] + nFS = nx*ny + for i in range(0,nx): + for j in range(0,ny): + bem['p'][i*ny+j] = bem['p'][i*ny+j] + dt * bem['dpdt'][i*ny+j] + fs['pos'][i,j,2] = fs['pos'][i,j,2] + dt * fs['vel'][i,j,2] + # Since the inverse method returns significant errors near + # to the free surface borders, we will modify 3 area + # elements of the border such that the last one will be + # exactly the analytic solution. Also we will use it as + # numerical beach in order to disipate waves generated + # inside the domain (that will be refelceted otherwise) + # 1.- Corners + for i in range(0,4)+range(nx-4,nx): + if i in range(0,4): + fx = 1. - i/4. + else: + fx = (i - nx + 5) / 4. + for j in range(0,4)+range(ny-4,ny): + if j in range(0,4): + fy = 1. - j/4. + else: + fy = (j - ny + 5) / 4. + factor = max(fx,fy) + pos = fs['pos'][i,j] + phi = 0. + z = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*frec/k*np.cos(k*l - frec*(t+dt) + phase)*np.exp(k*pos[2]) + phi = phi + amp + amp = A*np.sin(k*l - frec*(t+dt) + phase) + z = z + amp + bem['p'][i*ny+j] = factor*phi + (1.-factor)*bem['p'][i*ny+j] + fs['pos'][i,j,2] = factor*z + (1.-factor)*fs['pos'][i,j,2] + # 2.- rows + for i in range(0,4)+range(nx-4,nx): + if i in range(0,4): + factor = 1. - i/4. + else: + factor = (i - nx + 5) / 4. + for j in range(4, ny-4): + pos = fs['pos'][i,j] + phi = 0. + z = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*frec/k*np.cos(k*l - frec*(t+dt) + phase)*np.exp(k*pos[2]) + phi = phi + amp + amp = A*np.sin(k*l - frec*(t+dt) + phase) + z = z + amp + bem['p'][i*ny+j] = factor*phi + (1.-factor)*bem['p'][i*ny+j] + fs['pos'][i,j,2] = factor*z + (1.-factor)*fs['pos'][i,j,2] + # 3.- columns + for j in range(0,4)+range(ny-4,ny): + if j in range(0,4): + factor = 1. - j/4. + else: + factor = (j - ny + 5) / 4. + for i in range(4, nx-4): + pos = fs['pos'][i,j] + phi = 0. + z = 0. + for w in waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*frec/k*np.cos(k*l - frec*(t+dt) + phase)*np.exp(k*pos[2]) + phi = phi + amp + amp = A*np.sin(k*l - frec*(t+dt) + phase) + z = z + amp + bem['p'][i*ny+j] = factor*phi + (1.-factor)*bem['p'][i*ny+j] + fs['pos'][i,j,2] = factor*z + (1.-factor)*fs['pos'][i,j,2] + + diff --git a/src/Mod/Ship/simRun/clSim/initialization.py b/src/Mod/Ship/simRun/clSim/initialization.py index 5e4f30417..02b1c1661 100644 --- a/src/Mod/Ship/simRun/clSim/initialization.py +++ b/src/Mod/Ship/simRun/clSim/initialization.py @@ -1,113 +1,229 @@ #*************************************************************************** -#* * -#* Copyright (c) 2011, 2012 * -#* Jose Luis Cercos Pita * -#* * +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * #* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU Lesser General Public License (LGPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* This program is distributed in the hope that it will be useful, * -#* but WITHOUT ANY WARRANTY; without even the implied warranty of * -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -#* GNU Library General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * +#* USA * +#* * #*************************************************************************** -# Simulation stuff -from Utils import * - -# pyOpenCL -import pyopencl as cl +# numpy import numpy as np +import FreeCAD -class perform: - def __init__(self, FSmesh, waves, context, queue): - """ Constructor, includes program loading. - @param FSmesh Initial free surface mesh. - @param waves Considered simulation waves (A,T,phi,heading). - @param context OpenCL context where apply. - @param queue OpenCL command queue. - """ - self.context = context - self.queue = queue - self.program = loadProgram(context, clPath() + "/simInit.cl") - self.loadData(FSmesh, waves) - self.execute() +grav=9.81 + +class simInitialization_cl: + def __init__(self, FSMesh, FSData, waves, Sea, context=None, queue=None): + """ Constructor. + @param FSMesh Initial free surface mesh. + @param FSData Dimensions data of the free surface mesh (L,B,Nx,Ny) + @param waves Considered simulation waves (A,T,phi,heading). + @param Sea Tuple with the number of free surfaces that must be repeated in each direction. + @param context OpenCL context where apply. Only for compatibility, + must be None. + @param queue OpenCL command queue. Only for compatibility, + must be None. + """ + self.context = context + self.queue = queue + self.loadData(FSMesh, FSData, waves, Sea) + self.execute() + # Compute time step + self.dt = 0.1 + for w in self.waves['data']: + if(self.dt > w[1]/200.0): + self.dt = w[1]/200.0 + + def loadData(self, FSMesh, FSData, waves, Sea): + """ Convert data to numpy format. + @param FSMesh Initial free surface mesh. + @param FSData Dimensions data of the free surface mesh (L,B,Nx,Ny) + @param waves Considered simulation waves (A,T,phi,heading). + @param Sea Tuple with the number of free surfaces that must be repeated in each direction. + """ + # Data will classified in four groups: + # Free surface: + # Is a key part of the simulation, so is + # separated from the rest of water involved + # elements. + # Sea: + # BEM method requires to artificially extend + # the free surface in order to send the bounds + # Inlet, Outlet, Left and Side to the infinite. + # Here is specified how many time must be + # repeated the free surface in order to get + # virtually infinite far bounds. + # Body: + # Is the main objective of the simulation. + # Waves: + # Data that is append as boundary condition. + # BEM: + # Used to solve the BEM problem and evolution. + + # -------------------------------------------- + # Free surface data + # N, Nx, Ny = Number of points in each + # direction + # pos = Positions + # vel = Velocities + # n = Normals + # area = Areas + # -------------------------------------------- + nx = len(FSMesh) + ny = len(FSMesh[0]) + L = FSData[0] + B = FSData[1] + dx = L/nx + dy = B/ny + p = np.zeros((nx,ny, 4), dtype=np.float32) + V = np.zeros((nx,ny, 4), dtype=np.float32) + n = np.zeros((nx,ny, 4), dtype=np.float32) + a = np.ndarray((nx,ny), dtype=np.float32) + for i in range(0, nx): + for j in range(0, ny): + pos = FSMesh[i][j].pos + normal = FSMesh[i][j].normal + area = FSMesh[i][j].area + p[i,j,0] = pos.x + p[i,j,1] = pos.y + p[i,j,2] = pos.z + p[i,j,3] = 1.0 + n[i,j,0] = normal.x + n[i,j,1] = normal.y + n[i,j,2] = normal.z + a[i,j] = area + self.fs = {'N':nx*ny, 'Nx':nx, 'Ny':ny, \ + 'L':L, 'B':B, 'dx':dx, 'dy':dy, \ + 'pos':p, 'vel':V, 'normal':n, 'area':a} + # -------------------------------------------- + # Sea data + # N, Nx, Ny = Number of free surfaces + # repetitions in each direction + # -------------------------------------------- + self.sea = {'N':Sea[0]*Sea[1], 'Nx':Sea[0], 'Ny':Sea[1]} + # -------------------------------------------- + # Body data + # N, Nx, Ny = Number of points in each + # direction + # pos = Positions + # vel = Velocities + # n = Normals + # area = Areas + # -------------------------------------------- + self.b = {'N':0, 'pos':None, 'vel':None, 'normal':None, 'area':None} + # -------------------------------------------- + # Waves data + # N = Number of waves + # data = Waves data + # -------------------------------------------- + nW = len(waves) + w = np.ndarray((nW, 4), dtype=np.float32) + for i in range(0,nW): + w[i,0] = waves[i][0] + w[i,1] = waves[i][1] + w[i,2] = waves[i][2] + w[i,3] = waves[i][3] + self.waves = {'N':nW, 'data':w} + # -------------------------------------------- + # BEM data + # N = nFS + nB + # A,B = Linear system matrix and vectors + # p = Velocity potentials (phi). + # gradp = Velocity potentials gradient + # (grad(phi)) projected over the + # normal + # dpdt = Velocity potentials time + # variation rate + # -------------------------------------------- + nFS = self.fs['N'] + nB = self.b['N'] + N = nFS + nB + A = np.zeros((N, N), dtype=np.float32) + B = np.zeros((N), dtype=np.float32) + p = np.zeros((N), dtype=np.float32) + gp = np.zeros((N), dtype=np.float32) + dpdt = np.zeros((N), dtype=np.float32) + self.bem = {'N':N, 'A':A, 'B':B, \ + 'p':p, 'gradp':gp, 'dpdt':dpdt } + + def execute(self): + """ Compute initial conditions. """ + # -------------------------------------------- + # Free surface initial condition. + # Since RK4 scheme starts on the end of + # previous step, we only write on last + # stage value (p4 and dp4) + # -------------------------------------------- + nx = self.fs['Nx'] + ny = self.fs['Ny'] + for i in range(0,nx): + for j in range(0,ny): + # Since initial values of the potencial, and this acceleration, + # depends on z, we need to compute first the positions. + self.fs['pos'][i,j][2] = 0. + self.bem['p'][i*ny+j] = 0. + self.bem['gradp'][i*ny+j] = 0. + for w in self.waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = self.fs['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = A*np.sin(k*l + phase) + self.fs['pos'][i,j][2] = self.fs['pos'][i,j][2] + amp + amp = - A*frec*np.cos(k*l + phase) + self.fs['vel'][i,j][2] = self.fs['vel'][i,j][2] + amp + # And now we can compute potentials. + for w in self.waves['data']: + A = w[0] + T = w[1] + phase = w[2] + heading = np.pi*w[3]/180.0 + wl = 0.5 * grav / np.pi * T*T + k = 2.0*np.pi/wl + frec = 2.0*np.pi/T + pos = self.fs['pos'][i,j] + l = pos[0]*np.cos(heading) + pos[1]*np.sin(heading) + amp = - A*frec/k*np.cos(k*l + phase)*np.exp(k*pos[2]) + self.bem['p'][i*ny+j] = self.bem['p'][i*ny+j] + amp + amp = - A*frec*np.cos(k*l + phase)*np.exp(k*pos[2]) + self.bem['gradp'][i*ny+j] = self.bem['gradp'][i*ny+j] + amp + + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- + """ + self.fs['pos'][i,j][2] = 0. + self.bem['p'][i*ny+j] = 0. + self.bem['gradp'][i*ny+j] = 0. + # We can do phi = Green's function + dx = self.fs['pos'][i,j][0] + dy = self.fs['pos'][i,j][1] + dz = 15.0 # An arbitrary value > 0 + self.bem['p'][i*ny+j] = 1. / (4. * np.pi * np.sqrt(dx*dx + dy*dy + dz*dz)) + self.bem['gradp'][i*ny+j] = - dz / (4. * np.pi * (dx*dx + dy*dy + dz*dz)**(1.5)) + """ + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- - def loadData(self, FSmesh, waves): - """ Convert data to numpy format, and create OpenCL - buffers. - @param FSmesh Initial free surface mesh. - @param waves Considered simulation waves (A,T,phi,heading). - """ - mf = cl.mem_flags - nx = len(FSmesh) - ny = len(FSmesh[0]) - nW = len(waves) - # Mesh data - p = np.ndarray((nx*ny, 4), dtype=np.float32) - n = np.ndarray((nx*ny, 4), dtype=np.float32) - a = np.ndarray((nx*ny, 1), dtype=np.float32) - for i in range(0, nx): - for j in range(0, ny): - id = i*ny + j - pos = FSmesh[i][j].pos - normal = FSmesh[i][j].normal - area = FSmesh[i][j].area - p[id,0] = pos.x - p[id,1] = pos.y - p[id,2] = pos.z - p[id,3] = 1. - n[id,0] = normal.x - n[id,1] = normal.y - n[id,2] = normal.z - n[id,3] = 0. - a[id,0] = area - p_cl = cl.Buffer(self.context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=p) - n_cl = cl.Buffer(self.context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=n) - a_cl = cl.Buffer(self.context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=a) - v_cl = cl.Buffer(self.context, mf.READ_WRITE, size = nx*ny*4 * np.dtype('float32').itemsize) - f_cl = cl.Buffer(self.context, mf.READ_WRITE, size = nx*ny*4 * np.dtype('float32').itemsize) - phi = cl.Buffer(self.context, mf.READ_WRITE, size = nx*ny * np.dtype('float32').itemsize) - Phi = cl.Buffer(self.context, mf.READ_WRITE, size = nx*ny * np.dtype('float32').itemsize) - self.fs = {'Nx':nx, 'Ny':ny, 'pos':p_cl, 'vel':v_cl, 'acc':f_cl, \ - 'normal':n_cl, 'area':a_cl, 'velPot':phi, 'accPot':Phi} - # Waves data - w = np.ndarray((nW, 4), dtype=np.float32) - for i in range(0,nW): - w[i,0] = waves[i][0] - w[i,1] = waves[i][1] - w[i,2] = waves[i][2] - w[i,3] = waves[i][3] - w_cl = cl.Buffer(self.context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=w) - self.waves = {'N':nW, 'data':w_cl} - # Ensure that all data has been written - self.queue.finish() - def execute(self): - """ Compute initial conditions. """ - # Global size computation - N = np.ndarray((2, 1), dtype=np.uint32) - N[0] = self.fs['Nx'] - N[1] = self.fs['Ny'] - n = np.uint32(self.waves['N']) - gSize = (globalSize(N[0]),globalSize(N[1]),) - # Kernel arguments - kernelargs = (self.fs['pos'], - self.fs['vel'], - self.fs['acc'], - self.waves['data'], - self.fs['velPot'], - self.fs['accPot'], - N, n) - # Kernel launch - self.program.FS(self.queue, gSize, None, *(kernelargs)) - self.queue.finish() diff --git a/src/Mod/Ship/simRun/clSim/matrixGen.py b/src/Mod/Ship/simRun/clSim/matrixGen.py new file mode 100644 index 000000000..69430d9be --- /dev/null +++ b/src/Mod/Ship/simRun/clSim/matrixGen.py @@ -0,0 +1,184 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +# numpy +import numpy as np + +# pyOpenCL +import pyopencl as cl +import clUtils + +import FreeCAD +grav=9.81 + +class simMatrixGen_cl: + def __init__(self, context=None, queue=None): + """ Constructor. + @param context OpenCL context where apply. + @param queue OpenCL command queue. + """ + self.context = context + self.queue = queue + self.program = clUtils.loadProgram(context, clUtils.path() + "/matrixGen.cl") + # Create OpenCL objects as null objects, that we will generate + # at the first iteration + self.A = None + self.B = None + self.dB = None + self.pos = None + self.area = None + self.normal = None + self.p = None + self.gradp = None + + def execute(self, fs, waves, sea, bem, body, t): + """ Compute system matrix. + @param fs Free surface instance. + @param waves Waves instance. + @param sea Sea boundary instance. + @param bem Boundary Element Method instance. + @param body Body instance. + @param t Simulation time. + """ + # Create/set OpenCL buffers + self.setBuffers(fs,waves,sea,bem,body) + # Convert constant parameters + L = np.float32(fs['L']) + B = np.float32(fs['B']) + dx = np.float32(fs['dx']) + dy = np.float32(fs['dy']) + T = np.float32(t) + # Get dimensions for OpenCL execution + nx = np.uint32(fs['Nx']) + ny = np.uint32(fs['Ny']) + nFS = np.uint32(fs['N']) + nB = np.uint32(body['N']) + n = np.uint32(nFS + nB) + nSeax = np.int32(sea['Nx']) + nSeay = np.int32(sea['Ny']) + nW = np.uint32(waves['N']) + # Call OpenCL to work + gSize = (clUtils.globalSize(n),) + kernelargs = (self.A, + self.B, + self.pos, + self.area, + self.normal, + self.bem_p, + self.bem_dp, + self.waves, + L, + B, + dx, + dy, + T, + nx, + ny, + nFS, + nB, + n, + nSeax, + nSeay, + nW) + self.program.matrixGen(self.queue, gSize, None, *(kernelargs)) + self.queue.finish() + # Read output data + events = [] + events.append(cl.enqueue_read_buffer(self.queue, self.A, bem['A'].reshape((n*n)))) + events.append(cl.enqueue_read_buffer(self.queue, self.B, bem['B'])) + # events.append(cl.enqueue_read_buffer(self.queue, self.dB, bem['dB'])) + for e in events: + e.wait() + + + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- + """ + for i in range(0,fs['Nx']): + for j in range(0,fs['Ny']): + x = fs['pos'][i,j,0] + y = fs['pos'][i,j,1] + FreeCAD.Console.PrintMessage("pos = {0},{1}\n".format(x,y)) + A = np.dot(bem['A'][i*fs['Ny'] + j,:], bem['gradp'][:]) + B = bem['B'][i*fs['Ny'] + j] + phi = 2.0 * (B - A) + bem['p'][i*fs['Ny'] + j] = phi + """ + + # -------------------------------------------------------- + # Debugging + # -------------------------------------------------------- + return + + def setBuffers(self, fs, waves, sea, bem, body): + """ Create/set OpenCL required buffers. + @param fs Free surface instance. + @param waves Waves instance. + @param sea Sea boundary instance. + @param bem Boundary Element Method instance. + @param body Body instance. + """ + # Get dimensions + nFS = fs['N'] + nB = body['N'] + n = nFS + nB + nW = waves['N'] + # Generate arrays for positions, areas and normals + pos = np.zeros((n, 4), dtype=np.float32) + area = np.zeros((n ), dtype=np.float32) + normal = np.zeros((n, 4), dtype=np.float32) + p = np.zeros((n ), dtype=np.float32) + dp = np.zeros((n ), dtype=np.float32) + w = np.zeros((nW,4), dtype=np.float32) + pos[0:nFS] = fs['pos'].reshape((nFS,4)) + area[0:nFS] = fs['area'].reshape((nFS)) + normal[0:nFS] = fs['normal'].reshape((nFS,4)) + nx = fs['Nx'] + ny = fs['Ny'] + p[0:n] = bem['p'] + dp[0:n] = bem['gradp'] + w[0:nW] = waves['data'] + # Create OpenCL objects if not already generated + if not self.A: + mf = cl.mem_flags + self.A = cl.Buffer( self.context, mf.WRITE_ONLY, size = n*n * np.dtype('float32').itemsize ) + self.B = cl.Buffer( self.context, mf.WRITE_ONLY, size = n * np.dtype('float32').itemsize ) + self.dB = cl.Buffer( self.context, mf.WRITE_ONLY, size = n * np.dtype('float32').itemsize ) + self.pos = cl.Buffer( self.context, mf.READ_ONLY, size = n*4 * np.dtype('float32').itemsize ) + self.area = cl.Buffer( self.context, mf.READ_ONLY, size = n * np.dtype('float32').itemsize ) + self.normal = cl.Buffer( self.context, mf.READ_ONLY, size = n*4 * np.dtype('float32').itemsize ) + self.bem_p = cl.Buffer( self.context, mf.READ_ONLY, size = n * np.dtype('float32').itemsize ) + self.bem_dp = cl.Buffer( self.context, mf.READ_ONLY, size = n * np.dtype('float32').itemsize ) + self.waves = cl.Buffer( self.context, mf.READ_ONLY, size = nW*4 * np.dtype('float32').itemsize ) + # Transfer data to buffers + events = [] + events.append(cl.enqueue_write_buffer(self.queue, self.pos, pos)) + events.append(cl.enqueue_write_buffer(self.queue, self.area, area)) + events.append(cl.enqueue_write_buffer(self.queue, self.normal, normal)) + events.append(cl.enqueue_write_buffer(self.queue, self.bem_p, p)) + events.append(cl.enqueue_write_buffer(self.queue, self.bem_dp, dp)) + events.append(cl.enqueue_write_buffer(self.queue, self.waves, w)) + for e in events: + e.wait() + diff --git a/src/Mod/Ship/simRun/theory/abstract.tex b/src/Mod/Ship/simRun/theory/abstract.tex new file mode 100644 index 000000000..3aa57fbb2 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/abstract.tex @@ -0,0 +1,56 @@ +\chapter{Introduction} +\label{s:introduction} +% +\section{Objective} +% +The objective of this document is introduce briefly how the waves of the +seakeeping simulator are propagated.\rc +% +In the seakeeping simulator Boundary Elements Method (BEM) will be used, +that is detailed described in several books, like \citet{bem_2007}. +\citet{vinayan2007} gives a detailed description of the propagation +of waves in a 2D case, and is a good starting point.\rc +% +We will start briefly describing the governing equations in order to can +start working with the 2D problem. First the incident waves over our +computational domain will be described, introducing also the potential, +discussing then the BEM applied to this case. As we will see the Laplace +problem in the 2D case will not be really useful for us.\rc +% +After that we can start working in the 3D case, that is our real objective. +The incident waves will be rewritten, and the Laplace problem and the BEM +application purposed again. +% +\chapter{Governing equations} +\label{s:governing_equations} +% +Assuming no viscous fluid (that allows to transform Navier-Stokes +equations into Euler ones), and imposing an initial condition such +that\footnote{With no viscous fluid this condition is preserved along the +time}: +% +\begin{eqnarray} + \rotational \bs{u} = 0 +\end{eqnarray} +% +The fluid velocity derives from a scalar function potential $\phi$ +% +\begin{eqnarray} + \label{eq:governing_equations:v_potential} + \gradient \phi = \bs{u} +\end{eqnarray} +% +Then the Navier-Stokes equations can be rewriten as a Laplacian problem +and Bernoulli equation: +% +\begin{eqnarray} + \label{eq:governing_equations:laplace} + \laplacian \phi = & 0 + \\ + \label{eq:governing_equations:bernoulli} + p = & - \rho \left( \vert \bs{u} \vert^2 + g \bs{z} \right) +\end{eqnarray} +% +And in order to solve the Laplace problem \ref{eq:governing_equations:laplace} +we will use the BEM as described by \citet{bem_2007}. +% \ No newline at end of file diff --git a/src/Mod/Ship/simRun/theory/bib.bib b/src/Mod/Ship/simRun/theory/bib.bib new file mode 100644 index 000000000..ed2200659 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/bib.bib @@ -0,0 +1,91 @@ +@Comment{THE COMMENTS IN BIBTEX ARE IMPLEMENTED THIS WAY!} +@STRING(OE="Ocean Engineering") +@STRING{AA = "Astron. Astrophys."} +@STRING{ACA = "Extraits des comptes rendus des s\'eances de l'Acad\'emie des Sciences"} +@STRING{AG = "Adv. Geophys."} +@STRING{AIAAJ = "AIAA J."} +@STRING{AIAAP = "AIAA Pap."} +@STRING{AMR = "Appl. Mech. Rev."} +@STRING{ARFM = "Annu. Rev. Fluid Mech."} +@STRING{ARAA = "Annu. Rev. Astron. Astrophy."} +@STRING{AMCFD = "Advanced Methods for Computational Fluid Dynamics"} +@STRING{AMC = "Applied Mathematics and Computations"} +@STRING{ANM = "Applied Numerical Mathematics"} +@STRING{BAPS = "Bull. Am. Phys. Soc."} +@STRING{CUP = "Cambridge University Press"} +@STRING{CMAME = "Comput. Methods Appl. Mech. Engrg."} +@STRING{DI = "Dantec Information"} +@STRING{EF = "Expts. Fluids"} +@STRING{EJMF = "Eur. J. Mech. B/Fluids"} +@STRING{IJHFF = "Int. J. Heat and Fluid Flow"} +@STRING{IJHMT = "Int. J. Heat and Mass Transfer"} +@STRING{IJIE = "Int. J. Impact Engng."} +@STRING{IJNMF = "Int. J. Numer. Methods Fluids"} +@STRING{INPG = "Institut National Polytechnique Grenoble"} +@STRING{JA = "J. Aircraft"} +@STRING{JAM = "J. Applied Mech."} +@STRING{JAMTP = "J. Appl. Mech. Tech. Phys."} +@STRING{JAP = "J. App. Phys."} +@STRING{JAS = "J. Atmos. Sci."} +@STRING{JCP = "J. Comp. Phys."} +@STRING{JCAM = "J. Computational and Applied Mathematics"} +@STRING{JFE = "J. Fluids Engine."} +@STRING{JFM = "J. Fluid Mech."} +@STRING{JHE = "J. Hydraulic Engineering"} +@STRING{JM = "Journal de M\'ecanique"} +@STRING{JOT = "Journal of Turbulence"} +@STRING{JRNBS = "J. Res. Nat. Bur. Standards"} +@STRING{JTAM = "J. Theo. and Appl. Mech."} +@STRING{JWEIA = "J. Wind Engng. Indust. Aerodyn."} +@STRING{MC = "Math. Comp."} +@STRING{ME = "Meccanica"} +@STRING{MRP = "Mathematics Reports and Preprints"} +@STRING{MWR = "Mon. Weather Rev."} +@STRING{NACA = "NACA"} +@STRING{PFA = "Phys. Fluids A"} +@STRING{PFL = "Phys. Fluids Lett."} +@STRING{PFO = "Phys. Fluids"} +@STRING{PRA = "Phys. Review A"} +@STRING{PRE = "Phys. Review E"} +@STRING{PRL = "Phys. Review Lett."} +@STRING{PTRS = "Phil. Trans. Roy. Soc."} +@STRING{QJRMS = "Q. J. R. Meteorol. Soc."} +@STRING{RA = "Rech. Aeros."} +@STRING{RF = "Rapport Final"} +@STRING{SJAP = "Sov. J. Appl. Phys."} +@STRING{SSSR = "Dokl. Akad. Nauk. SSSR"} +@STRING{SV = "Springer-Verlag"} +@STRING{TAM = "Theoret. and Applied Mech."} +@STRING{TAJ = "The Astrophysical Journal"} +@STRING{TCFD = "Theoret. Comput. Fluid Dynamics"} +@STRING{EJBE = "Electronic Journal of Boundary Elements"} + + +@BOOK{bem_2007, + AUTHOR = {Whye-Teong Ang}, + EDITOR = {Universal Publishers}, + TITLE = {A Beginner’s Course in Boundary Element Methods}, + PUBLISHER = {Cambridge University Press, New York}, + YEAR = {2007}, + VOLUME = {}, + SERIES = {}, + ADDRESS = {}, + EDITION = {}, + MONTH = {} +} + +@ARTICLE{vinayan2007, + AUTHOR="Vinayan, V. and Kinnas, S. A. ", + TITLE="A BEM for the Propagation of Nonlinear Planar Free-surface Waves", + JOURNAL=EJBE, + VOLUME=5, + PAGES="17-40", + YEAR=2007, +} + +@PHDTHESIS{yang2004, + AUTHOR="Jinghai Yang", + TITLE="Time domain, nonlinear theories on ship motions", + SCHOOL="University of Hawaii", + YEAR=2004, +} diff --git a/src/Mod/Ship/simRun/theory/deep_water.wxmx b/src/Mod/Ship/simRun/theory/deep_water.wxmx new file mode 100644 index 000000000..7677b0dc5 Binary files /dev/null and b/src/Mod/Ship/simRun/theory/deep_water.wxmx differ diff --git a/src/Mod/Ship/simRun/theory/images/CC_88x31.png b/src/Mod/Ship/simRun/theory/images/CC_88x31.png new file mode 100644 index 000000000..49f272f82 Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/CC_88x31.png differ diff --git a/src/Mod/Ship/simRun/theory/images/Integral.png b/src/Mod/Ship/simRun/theory/images/Integral.png new file mode 100644 index 000000000..2e53f03a4 Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/Integral.png differ diff --git a/src/Mod/Ship/simRun/theory/images/Integral.svg b/src/Mod/Ship/simRun/theory/images/Integral.svg new file mode 100644 index 000000000..4efb0760e --- /dev/null +++ b/src/Mod/Ship/simRun/theory/images/Integral.svg @@ -0,0 +1,911 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dx + Dx + Z(xa,ya) + + + + + + + + + Z(xa-Dx,ya+Dy) + + + + + + + + + Z(xa-Dx,ya) + + + + + + + + + Z(xa-Dx,ya-Dy) + + + + + Z(xa-Dx,ya+Dy) + Z(xa-Dx,ya) + + + + + + + + + Z(xa-Dx,ya-Dy) + + + + + Z(xa-Dx,ya+Dy) + + + + + Z(xa-Dx,ya-Dy) + + + + + dy + Dy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Ship/simRun/theory/images/Omega.png b/src/Mod/Ship/simRun/theory/images/Omega.png new file mode 100644 index 000000000..e9ed71171 Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/Omega.png differ diff --git a/src/Mod/Ship/simRun/theory/images/Omega.svg b/src/Mod/Ship/simRun/theory/images/Omega.svg new file mode 100644 index 000000000..00c10c872 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/images/Omega.svg @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + δΩBottom + δΩFS + Ω + + + + + + + δΩInlet + δΩOutlet + + diff --git a/src/Mod/Ship/simRun/theory/images/Omega2.png b/src/Mod/Ship/simRun/theory/images/Omega2.png new file mode 100644 index 000000000..0ebe7e232 Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/Omega2.png differ diff --git a/src/Mod/Ship/simRun/theory/images/Omega2.svg b/src/Mod/Ship/simRun/theory/images/Omega2.svg new file mode 100644 index 000000000..c93a3d4fb --- /dev/null +++ b/src/Mod/Ship/simRun/theory/images/Omega2.svg @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + δΩBottom + δΩFS + Ω + + + δΩInlet + δΩOutlet + + + δΩFS,I + δΩFS,I + + diff --git a/src/Mod/Ship/simRun/theory/images/test_bem.png b/src/Mod/Ship/simRun/theory/images/test_bem.png new file mode 100644 index 000000000..4d8d097bd Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/test_bem.png differ diff --git a/src/Mod/Ship/simRun/theory/images/test_direct.png b/src/Mod/Ship/simRun/theory/images/test_direct.png new file mode 100644 index 000000000..1e8e22e9e Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/test_direct.png differ diff --git a/src/Mod/Ship/simRun/theory/images/test_wave.png b/src/Mod/Ship/simRun/theory/images/test_wave.png new file mode 100644 index 000000000..ac983211e Binary files /dev/null and b/src/Mod/Ship/simRun/theory/images/test_wave.png differ diff --git a/src/Mod/Ship/simRun/theory/laplace2D.tex b/src/Mod/Ship/simRun/theory/laplace2D.tex new file mode 100644 index 000000000..e0ebcc9ed --- /dev/null +++ b/src/Mod/Ship/simRun/theory/laplace2D.tex @@ -0,0 +1,196 @@ +\chapter{Waves propagations in 2D plane} +\label{s:laplace2D} +% +\section{General} +% +In this chapter the 2D case will be discussed, looking for +a method to solve the BEM using only the information about +the free surface. As we will see is not possible to do it in +the 2D case, and may move to 3D case. +% +\section{Incident waves} +% +First for all we need to describe the incident waves, that are +the waves out of our computational domain. +% +\begin{eqnarray} + \label{eq:laplace2D:incident_waves} + \begin{array}{rcl} + z(x,t) & = & \dsty{\sum_{j=1}^{n_{waves}}} a_j \sin\left(k_j x - \omega_j t + \delta_j \right) + \\ + v_z(x,t) & = & \dsty{\sum_{j=1}^{n_{waves}}} - a_j \omega_j \cos\left(k_j x - \omega_j t + \delta_j \right) + \end{array} +\end{eqnarray} +% +The phase velocity for the waves in the most general case can +be written as: +% +\begin{eqnarray} + \label{eq:laplace2D:c_general} + c_j = \sqrt{\frac{g \lambda}{2 \pi} \tanh\left(\frac{2 \pi d}{\lambda}\right)} +\end{eqnarray} +% +But if the water depth is such that $d > \frac{\lambda}{2}$ we +can rewrite the phase velocity \ref{eq:laplace2D:c_general} as +% +\begin{eqnarray} + \label{eq:laplace2D:c_deep} + c_j^{deep} = \sqrt{\frac{g \lambda}{2 \pi}} +\end{eqnarray} +% +By definition the phase velocity can be also written as +$c_j = \omega_j / k_j$, so we can relate the wave length +with the period +% +\begin{eqnarray} + \label{eq:laplace2D:k_w} + k_j = \frac{\omega_j^2}{g} +\end{eqnarray} +% +Regarding the velocity potential, let us define it as +% +\begin{eqnarray} + \label{eq:laplace2D:incident_waves_potential} + \phi(x,z,t) = \sum_{j=1}^{n_{waves}} - \frac{a_j \omega_j}{k_j} + \cos\left(k_j x - \omega_j t + \delta_j \right) + \exp\left(k_j z\right) +\end{eqnarray} +% +such that +% +\begin{eqnarray*} + \begin{array}{rcl} + \dsty{\left. \frac{\partial \phi(x,z,t)}{\partial z} \right\vert_{z=0}} & = & v_z(x,t) + \\ + \laplacian \phi(x,z,t) & = & 0 + \end{array} +\end{eqnarray*} +% +This potential is valid for deep water waves if the exponent +$k_j z$ is small enough. $z$ can take values of the order of +$\mathcal{O}(a_j)$, so this method is valid for waves which +the length is such that +% +\begin{eqnarray*} + \lambda_j \gg 2 \pi a_j +\end{eqnarray*} +% +\section{BEM applied to Laplace 2D problem} +\label{ss:laplace2D:bem} +% +We have defined the sea waves outside from our computational domain +$\Omega$, where the waves can be perturbed by floating objects. In +the figure \ref{fig:ss:laplace2D:bem} a schematic view of the +computational domain is shown.\rc +% +The BEM is based in the reciprocal relation application +% +\begin{eqnarray} + \label{eq:laplace2D:reciprocal_relation} + \lambda(X, Z) \phi(X, Z; t) = \int_{\partial \Omega} + \phi(x, z; t) \frac{\partial G(x, z, X, Z)}{\partial \bs{n}(x, z)} - + G(x, z, X, Z) \frac{\partial \phi(x, z; t)}{\partial \bs{n}(x, z)} + \mbox{d} s(x, z) +\end{eqnarray} +% +Where +% +\begin{eqnarray*} +\lambda(X, Z) = \left\lbrace +\begin{array}{lcl} + 1 & \mbox{if} & (X, Z) \in \Omega + \\ + \frac{1}{2} & \mbox{if} & (X, Z) \in \partial \Omega + \\ + 0 & \mbox{if} & (X, Z) \not \in \Omega +\end{array} +\right. +\end{eqnarray*} +% +$G(x, z, X, Z)$ is the Green's function (a particular solution +of the Laplace equation), and the derivative respect to normal +denotes the gradient of the function projected over the normal. +% +\begin{eqnarray*} +\frac{\partial f(x, z)}{\partial \bs{n}(x, z)} = +\gradient f(x, z) \cdot \bs{n}(x, z) +\end{eqnarray*} +% +Hereinafter let we define the function $H(x, z, X, Z)$ as +% +\begin{eqnarray*} +H(x, z, X, Z) = \frac{\partial G(x, z, X, Z)}{\partial \bs{n}(x, z)} +\end{eqnarray*} +% +Therefore BEM allows to, knowing along the contour the potential or +the gradient, not both, compute the another one. The parts of the +contour where we know both potential and the gradient will enter in +the method as part of the independent term in the linear system of +equations.\rc +% +Since we know the potential out from the domain $\Omega$, we can know +the potential and the gradient along the inlet $\partial \Omega_{Inlet}$ +and outlet $\partial \Omega_{Outlet}$.\rc +% +Regarding the bottom, we only can assert that the gradient along the +normal, that is the vertical velocity of the fluid, is null.\rc +% +Therefore, the inlet and outlet have relatively good properties because +we know all the data, so will not be additional work into the linear +system matrix that must be inverted, but the bottom don't have this +desirable property.\rc +% +Nevertheless, since the geometry of the inlet and +outlet is different from the free surface, and the bottom must be +explicitly considered, all the contour must be discretized with an +undesirable computational cost associated.\rc +% +So we are interested to know if we can replace our computational +domain $\Omega$ for other where only the free surface contour is +involved, moving the Inlet, the Outlet, and the bottom infinity far. We +could change our computational domain if the Green's function, and their +gradient, goes to zero as we go far enough. In 2D we can found a Green's +function for the Laplace problem such that +% +\begin{eqnarray} + \label{eq:laplace2D:g} + G(x, z, X, Z) = & \dsty{\frac{1}{4 \pi}} \log \left( \left(x - X\right)^2 + \left(z - Z\right)^2 \right) + \\ + \label{eq:laplace2D:h} + H(x, z, X, Z) = & \dsty{\frac{1}{2 \pi}} \frac{\left( x - X, z - Z \right)}{\left( \left(x - X\right)^2 + \left(z - Z\right)^2 \right)} +\end{eqnarray} +% +Whose limits when the radius goes to infinite can be found +% +\begin{eqnarray} + \label{eq:laplace2D:limit_g} + \lim_{(x-X)^2 + (z-Z)^2 \to \infty} G(x, z, X, Z) = & \infty + \\ + \label{eq:laplace2D:limit_h} + \lim_{(x-X)^2 + (z-Z)^2 \to \infty} H(x, z, X, Z) = & 0 +\end{eqnarray} +% +So in the 2D Laplace problem, if we try to send the Inlet, the Outlet, +or the bottom to the infinite we can't use BEM because the Green's +function is not well defined, diverging with the distance. +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.4\textwidth]{Omega} + \caption{Computational domain $\Omega$} + \label{fig:ss:laplace2D:bem} +\end{figure} +% +\section{Conclusions to Laplace 2D problem} +\label{ss:laplace2D:conclusions} +% +We have briefly discussed the wave propagation problem using BEM in a 2D +case, seeing that in this case we need to consider all the contours, +including the Inlet, the Outlet, and the bottom. This approach is +successfully applied in a 2D problem in the reference \citep{vinayan2007}. +In 2D problem considering all the contours is not a heavy problem because +the number of nodes is usually small compared with a 3D case, but in a 3D +problem a computational less expensive method is required.\rc +% +We will not continue with the 2D Laplace problem because the solution will +not be useful for us in the 3D case. \ No newline at end of file diff --git a/src/Mod/Ship/simRun/theory/laplace3D.tex b/src/Mod/Ship/simRun/theory/laplace3D.tex new file mode 100644 index 000000000..96854c14b --- /dev/null +++ b/src/Mod/Ship/simRun/theory/laplace3D.tex @@ -0,0 +1,642 @@ +\chapter{Waves propagations in 3D} +\label{s:laplace3D} +% +\section{General} +% +In this chapter the 3D case will be discussed, looking for +a method to solve the BEM using only the information about +the free surface. +% +This case is our main objective in order to can setup 6-DOF +seakeeping simulations. +% +\section{Incident waves} +% +We can rewrite the sea waves system outside from our +computational domain $\Omega$ +% +\begin{eqnarray} + \label{eq:laplace3D:incident_waves} + \begin{array}{rcl} + z(x,y,t) & = & \dsty{\sum_{j=1}^{n_{waves}}} a_j \sin\left(k_j \left(x \cos(\beta) + y \sin(\beta)\right) - \omega_j t + \delta_j \right) + \\ + v_z(x,y,t) & = & \dsty{\sum_{j=1}^{n_{waves}}} - a_j \omega_j \cos\left(k_j \left(x \cos(\beta) + y \sin(\beta)\right) - \omega_j t + \delta_j \right) + \\ + \phi(x,z,t) & = & \dsty{\sum_{j=1}^{n_{waves}}} - \frac{a_j \omega_j}{k_j} + \cos\left(k_j \left(x \cos(\beta) + y \sin(\beta)\right) - \omega_j t + \delta_j \right) + \exp\left(k_j z\right) + \\ + k_j & = & \dsty{\frac{\omega_j^2}{g}} + \end{array} +\end{eqnarray} +% +Where $\beta$ is the heading angle, being 0 for stern waves. For +this wave system still being valid the phase velocity from the +equation \ref{eq:laplace2D:c_deep}. The purposed potential is +compatible with the Laplace equation +\ref{eq:governing_equations:laplace} as well. +% +\section{BEM applied to Laplace 3D problem} +\label{ss:laplace3D:bem} +% +\subsection{Computational domain} +\label{sss:laplace3D:computational_domain} +% +We have a domain similar to the shown in the figure +\ref{fig:ss:laplace2D:bem}, but in this case 2 more boundaries +must be considered in the missed direction, that we will call +$\partial \Omega_{Front}$ and $\partial \Omega_{Back}$. As in +the 2D case we will apply the reciprocal relation +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation} + \lambda(X, Y, Z) \phi(X, Y, Z; t) = \int_{\partial \Omega} + \phi(x, y, z; t) \frac{\partial G(x, y, z, X, Y, Z)}{\partial \bs{n}(x, y, z)} - + G(x, y, z, X, Y, Z) \frac{\partial \phi(x, y, z; t)}{\partial \bs{n}(x, y, z)} + \mbox{d} s(x, y, z) +\end{eqnarray} +% +We are focused into compute the gradient of the velocity +potential along the free surface knowing the velocity +potential value in each point one. Let we define the +function $H(x,y,z,X,Y,Z)$ again +% +\begin{eqnarray*} +H(x, y, z, X, Y, Z) = \frac{\partial G(x, y, z, X, Y, Z)}{\partial \bs{n}(x, y, z)} +\end{eqnarray*} +% +As in the Laplace equation for the 2D case, described in the +chapter \ref{s:laplace2D}, we want to expand the domain $\Omega$ +such that all the boundaries except the free surface will the +infinity far, adding the boundary $\partial \Omega_{FS,I}$, where +we know the velocity potential and their gradient from +\ref{eq:laplace3D:incident_waves}. In the figure +\ref{fig:ss:laplace3D:bem} a schematic view of the expanded +domain can be seen.\rc +% +The main advantage is that, as happens with the Inlet and the +Outlet, we know all the needed data about the velocity potential, +so we can significantly reduce the linear system matrix dimensions, +and as happens with the bottom, the geometry is so quite similar +to the $\Omega_{FS}$ one, so no additional discretization or memory +storage is needed.\rc +% +This trick will only works if the Green's function $G(x,y,z,X,Y,Z)$, +and their gradient $H(x,y,z,X,Y,Z)$, goes to zero as $(x,y,z)$ goes +to infinite. In 3D Laplace problems we can use the following Green's +function: +% +\begin{eqnarray} + \label{eq:laplace3D:g} + G(x,y,z,X,Y,Z) = & \dsty{\frac{1}{\sqrt{\left(x - X\right)^2 + \left(y - Y\right)^2 + \left(z - Z\right)^2}}} + \\ + \label{eq:laplace3D:h} + H(x,y,z,X,Y,Z) = & - \dsty{\frac{\left( x - X, y - Y, z - Z \right)}{\left(\left(x - X\right)^2 + \left(y - Y\right)^2 + \left(z - Z\right)^2\right)^{3/2}}} +\end{eqnarray} +% +That in the limit +% +\begin{eqnarray} + \label{eq:laplace3D:limit_g} + \lim_{(x-X)^2 + (y-Y)^2 + (z-Z)^2 \to \infty} G(x,y,z,X,Y,Z) = & 0 + \\ + \label{eq:laplace3D:limit_h} + \lim_{(x-X)^2 + (y-Y)^2 + (z-Z)^2 \to \infty} H(x,y,z,X,Y,Z) = & 0 +\end{eqnarray} +% +So in this case, if the potential of the incidents waves is +a good function along all the free surface we can move from +the domain shown in the figure \ref{fig:ss:laplace2D:bem} to +the shown in the figure \ref{fig:ss:laplace3D:bem}, due to +along the other boundaries the Green's functions $G(x,y,z,X,Y,Z)$ +and $H(x,y,z,X,Y,Z)$ are nulls, not computing in the equation +\ref{eq:laplace2D:reciprocal_relation}. +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.6\textwidth]{Omega2} + \caption{Computational domain $\Omega$} + \label{fig:ss:laplace3D:bem} +\end{figure} +% +\subsection{Boundary conditions (BC)} +\label{sss:laplace3D:BC} +% +In order to can purpose an evolution process we need to use +the boundary conditions along the free surface. In the most +general way we can rewrite the Bernoulli equation, that is +the dynamic free surface boundary condition (DFSBC), as (see +\citep{yang2004}): +% +\begin{eqnarray} + \label{eq:laplace3D:FS_Bernoulli} + \frac{\mbox{D} \phi(x,y,z;t)}{\mbox{D} t} = + \frac{1}{2}\left\vert \gradient \phi \right\vert^2 + - g z(x,y;t) + - \frac{p_0}{\rho} + - \bs{U}(t) \cdot \gradient \phi(x,y,z;t) + - \frac{\partial \bs{U}(t)}{\partial t} \cdot (x,y,0) +\end{eqnarray} +% +Where $p_0$ is the atmospheric pressure, that we will consider +null, and $\bs{U}(t)$ is the ship velocity, in this case will be +a null vector. Since the material derivative denotes +% +\begin{eqnarray*} + \frac{\mbox{D} f}{\mbox{D} t} = + \frac{\partial f}{\partial t} + \gradient{\phi(x,y,z;t)} \cdot \gradient{f} +\end{eqnarray*} +% +We can rewrite the dynamic boundary condition for this specific case as +% +\begin{eqnarray} + \label{eq:laplace3D:DFSBC} + \frac{\partial \phi(x,y,z;t)}{\partial t} = + - \frac{1}{2}\left\vert \gradient \phi \right\vert^2 + - g z(x,y;t) +\end{eqnarray} +% +Regarding the kinematic free surface boundary condition (KFSBC), in the +most general way we can write that +% +\begin{eqnarray} + \label{eq:laplace3D:FS_Kinematic} + \frac{\mbox{D} z(x,y;t)}{\mbox{D} t} = + \frac{\partial \phi(x,y,z;t)}{\partial z} + - \bs{U}(t) \cdot \gradient z(x,y;t) +\end{eqnarray} +% +Where we can expand the material derivative, writing the KFSBC for this +specific case +% +\begin{eqnarray} + \label{eq:laplace3D:KFSBC} + \frac{\partial z(x,y;t)}{\partial t} = + \frac{\partial \phi(x,y,z;t)}{\partial z} + - \gradient{\phi(x,y,z;t)} \cdot \gradient{z(x,y;t)} +\end{eqnarray} +% +\subsection{Time integration scheme} +\label{sss:laplace3D:TimeIntegration} +% +We may start the simulation in a initial condition where we know the +full free surface shape and velocity potential, including the gradients. +% +\begin{eqnarray} + \label{eq:laplace3D:IC} + \begin{array}{lcl} + z(x,y;t=0) & = & z_0(x,y) + \\ + \phi(x,y,z;t=0) & = & \phi_0(x,y,z) + \\ + \gradient z(x,y;t=0) & = & \gradient z_0(x,y) + \\ + \gradient \phi(x,y,z;t=0) & = & \gradient \phi_0(x,y,z) + \end{array} +\end{eqnarray} +% +In the computational part of the free surface is enough to know the free +surface shape $z_0(x,y)$ and the velocity potential $\phi_0(x,y,z)$.\rc +% +For simplicity we will use an Euler's integration scheme, but the same +method can be easily applied for any other explicit time integrator, like +the Adams-Bashforth ones.\rc +% +In each time step we start knowing the shape of the free surface, and the +velocity potential, and basically we want to compute these fields for the +next time step. To do it the following steps are followed: +% +\begin{enumerate} + \item We use BEM to compute the velocity potential gradient, as will be + described in the section \ref{sss:laplace3D:bem_solve}. + \begin{eqnarray} + \label{eq:laplace3D:time_march:bem} + \gradient{\phi(x,y,z;t)} = \mbox{BEM}\left(\phi(x,y,z;t), z(x,y;t)\right) + \end{eqnarray} + \item We use the KFSBC to compute the derivative of the free surface + elevation, and the DFSBC to know the derivative of the velocity potential. + \begin{eqnarray} + \label{eq:laplace3D:time_march:dzdt} + \frac{\partial z(x,y;t)}{\partial t} = & + \mbox{KFSBC}\left(\gradient{\phi(x,y,z;t)}\right) + \\ + \label{eq:laplace3D:time_march:dphidt} + \frac{\partial \phi(x,y,z;t)}{\partial t} = & + \mbox{DFSBC}\left(z(x,y;t), \gradient{\phi(x,y,z;t)}\right) + \end{eqnarray} + \item And then we can perform the time integration. + \begin{eqnarray} + \label{eq:laplace3D:time_march:z_integrate} + z(x,y;t + \Delta t) = & + z(x,y;t) + + \Delta t \dsty \frac{\partial z(x,y;t)}{\partial t} + \\ + \label{eq:laplace3D:time_march:phi_integrate} + \phi(x,y,z;t + \Delta t) = & + \phi(x,y,z;t + \Delta t) + + \Delta t \dsty \frac{\partial \phi(x,y,z;t)}{\partial t} + \end{eqnarray} +\end{enumerate} +% +\subsection{Discrete Laplace solution using the BEM} +\label{sss:laplace3D:bem_solve} +% +As we have seen in the previous sections we want to use the BEM in order +to compute the velocity potential gradient in the free surface from the +velocity potential value known. In the equation +\ref{eq:laplace3D:reciprocal_relation} we can starting dividing the domain +contour as shown in the figure \ref{fig:ss:laplace3D:bem}, getting the +computational one, denoted by $\partial \Omega_{FS}$, and +the extension one, denoted by $\partial \Omega_{FS,I}$, where the free +surface and the velocity potential and gradient is known for all the +time instants. +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:002} + \begin{array}{l} + \frac{1}{2} \phi(X, Y, Z; t) = + \\ + \dsty \int_{\partial \Omega_{FS}} + \phi(x, y, z; t) \frac{\partial G(x, y, z, X, Y, Z)}{\partial \bs{n}(x, y, z)} - + G(x, y, z, X, Y, Z) \frac{\partial \phi(x, y, z; t)}{\partial \bs{n}(x, y, z)} + \mbox{d} s(x, y, z) + \\ + \dsty \int_{\partial \Omega_{FS,I}} + \phi(x, y, z; t) \frac{\partial G(x, y, z, X, Y, Z)}{\partial \bs{n}(x, y, z)} - + G(x, y, z, X, Y, Z) \frac{\partial \phi(x, y, z; t)}{\partial \bs{n}(x, y, z)} + \mbox{d} s(x, y, z) + \end{array} +\end{eqnarray} +% +Where we already assumed that $(x,y,z) \in \partial \Omega$. We can start discretizing +the velocity potential, assuming that the potential and their gradient changes smoothly +enough. The our contours can be divided according to the grid: +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:003} + \begin{array}{rl} + \frac{1}{2} \phi_a = & + \dsty \sum_{b=1}^{n_{FS}} + \phi_b + \dsty \int_{\partial \Omega_{FS}^b} H(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + - + \dsty \sum_{b=1}^{n_{FS}} + \frac{\partial \phi_b}{\partial \bs{n}_b} + \dsty \int_{\partial \Omega_{FS}^b} G(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + \\ + & + \dsty \sum_{b=1}^{n_{FS,I}} + \phi_b + \dsty \int_{\partial \Omega_{FS,I}^b} H(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + - + \dsty \sum_{b=1}^{n_{FS,I}} + \frac{\partial \phi_b}{\partial \bs{n}_b} + \dsty \int_{\partial \Omega_{FS,I}^b} G(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + \end{array} +\end{eqnarray} +% +The functions $G(x,y,z,X,Y,Z)$ and $H(x,y,z,X,Y,Z)$, according to the equations +\ref{eq:laplace3D:g} and \ref{eq:laplace3D:h}, are well defined in all the +subintervals where $(x,y,z) \neq (X,Y,Z)$, and are so quite smooth, so we will +change all the integrals that accomplish it for point evaluations. +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:004} + \begin{array}{rl} + \frac{1}{2} \phi_a = & + \phi_a + \dsty \int_{\partial \Omega_{FS}^a} H(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + - + \frac{\partial \phi_a}{\partial \bs{n}_a} + \dsty \int_{\partial \Omega_{FS}^a} G(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + \\ + & + \dsty \sum_{\substack{b = 1 \\ b \neq a}}^{n_{FS}} + \left( + \phi_b H_{ba} - \frac{\partial \phi_b}{\partial \bs{n}_b} G_{ba} + \right) S_b + + + \dsty \sum_{b=1}^{n_{FS,I}} + \left( + \phi_b H_{ba} - \frac{\partial \phi_b}{\partial \bs{n}_b} G_{ba} + \right) S_b + \end{array} +\end{eqnarray} +% +The remaining integrals must be treated carefully since the functions are +singular in the center of the interval. $H(x,y,z,X,Y,Z)$ is an odd function, +so the limit of the integral when the radius of the interval goes to zero +is null, being well defined. Regarding the function $G(x,y,z,X,Y,Z)$ is an +even function of order: +% +\begin{eqnarray*} + G(\bs{x},\bs{x_a}) = \mathcal{O}\left(\frac{1}{\vert \bs{x} - \bs{x_a}\vert}\right) +\end{eqnarray*} +% +Which their integral is defined if the function $z(x,y)$ is well defined +as well. So the remaining integrals can be numerically computed, being mindful +that: +% +\begin{enumerate} + \item Can't be evaluated at the point $\bs{x_a}$. + \item Changes too fast around the point $\bs{x_a}$. +\end{enumerate} +% +We will discuss later how to solve this integrals, for the moment we will define +new functions such that: +% +\begin{eqnarray} + \label{eq:laplace3D:g:hat} + \hat G_{ab} = + \left\lbrace \begin{array}{l} + \dsty \int_{\partial \Omega_{FS}^a} G(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}); \, \, \mbox{if} \, a = b + \\ + G(\bs{x_b},\bs{x_a}) S_b; \, \, \mbox{if} \, a \neq b + \end{array} \right. + \\ + \label{eq:laplace3D:h:hat} + \hat H_{ab} = + \left\lbrace \begin{array}{l} + \dsty \int_{\partial \Omega_{FS}^a} H(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}); \, \, \mbox{if} \, a = b + \\ + H(\bs{x_b},\bs{x_a}) S_b; \, \, \mbox{if} \, a \neq b + \end{array} \right. +\end{eqnarray} +% +So we can rewrite the equation \ref{eq:laplace3D:reciprocal_relation:004} +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:005} + \frac{1}{2} \phi_a + = + \dsty \sum_{b=1}^{n_{FS}} + \left( + \phi_b \hat H_{ba} - \frac{\partial \phi_b}{\partial \bs{n}_b} \hat G_{ba} + \right) + + + \dsty \sum_{b=1}^{n_{FS,I}} + \left( + \phi_b \hat H_{ba} - \frac{\partial \phi_b}{\partial \bs{n}_b} \hat G_{ba} + \right) +\end{eqnarray} +% +Where we can move all the terms of the computational free surface that affects +to the gradient of the velocity potential (that is the value that we want to +compute) to left hand side, and let all the other ones in the right hand side +of the equation +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:006} + \dsty \sum_{b \in \partial \Omega_{FS}} + \frac{\partial \phi_b}{\partial \bs{n}_b} \hat G_{ba} + = + - + \frac{1}{2} \phi_a + + + \dsty \sum_{b \in \partial \Omega_{FS}} + \phi_b \hat H_{ba} + + + \dsty \sum_{b \in \partial \Omega_{FS,I}} + \left( + \phi_b \hat H_{ba} - + \frac{\partial \phi_b}{\partial \bs{n}_b} \hat G_{ba} + \right) +\end{eqnarray} +% +The equation \ref{eq:laplace3D:reciprocal_relation:006}, that +has been written for the velocity potential at one point $\bs{x}_a$, +can be written for all the points of the free surface along +the computational domain using the matrix notation +% +\begin{eqnarray} + \label{eq:laplace3D:reciprocal_relation:007} + \mathcal{G} \left[ \frac{ \partial \phi}{\partial \bs{n}} \right] + = + \left( \mathcal{H} - \frac{1}{2} \mathcal{I} \right) \left[ \phi \right] + + + \mathcal{H}_{FS,I} \left[ \phi \right]_{FS,I} + - + \mathcal{G}_{FS,I} \left[ \frac{ \partial \phi}{\partial \bs{n}} \right]_{FS,I} +\end{eqnarray} +% +Note that the area of the elements $S_b$ has been included into +the matrices. The equation +\ref{eq:laplace3D:reciprocal_relation:007} is a linear system +of equations that can be numerically solved, either inverting +the matrix, or using an iterative method. The matrix inversion +is probably the best way for linear seakeeping codes, where the +same matrix will be ever used, but in this case the iterative +method is the faster way.\rc +% +The method described along this section allows to us to compute +the gradient of the velocity potential along the free surface +knowing the potential in the computational free surface, and both +velocity potential and the gradient along the extended free +surface. +% +\subsection{Integrals computation} +\label{sss:laplace3D:integrals} +% +In the equations \ref{eq:laplace3D:g:hat} and \ref{eq:laplace3D:g:hat} +we have introduced two inconvenient integrals. Even though the functions +are not well defined when $\bs{x} = \bs{x_a}$, their integrals it is. +For instance, if we can assume that the free surface is fully planar +($z = 0$), the integrals can be analytically computed +% +\begin{eqnarray*} + \dsty \int_{y_a - \delta y}^{y_a + \delta y} \int_{x_a - \delta x}^{x_a + \delta x} + G(x,y,0,x_a,y_a,0) + \, \mbox{d}x \, \mbox{d}y + = & + \dsty \frac{ + \delta x \, \, \mbox{asinh}\left(\frac{\delta y}{\delta x}\right) + + + \delta y \, \, \mbox{asinh}\left(\frac{\delta x}{\delta y}\right) + }{\pi} + \\ + \dsty \int_{y_a - \delta y}^{y_a + \delta y} \int_{x_a - \delta x}^{x_a + \delta x} + H(x,y,0,x_a,y_a,0) + \, \mbox{d}x \, \mbox{d}y + = & + 0 +\end{eqnarray*} +% +But can not be analytically computed for every function $z(x,y)$, being +necessary to compute it in a numerical way.\rc +% +In the figure \ref{fig:ss:laplace3D:integral} a schematic representation +of the integration method is shown. Let we want to compute the integral +for an element of the grid $(x_a,y_a)$, then we subdivide the element in +\textbf{a even number} of subelements of area $dx \cdot dy$, so we can +assert that any subelement will be evaluated in the point $(x_a,y_a)$, +but as near as we want because we can ever add more subelements; then we +can numerically approximate the integral by (here in after we will use +only the function $G(\bs{x}, bs{x_a})$, because same method can be applied +to the function $H(\bs{x}, bs{x_a})$). +% +\begin{eqnarray} + \label{eq:laplace3D:integral:g} + \dsty \int_{\partial \Omega_{FS}^a} G(\bs{x}, \bs{x}_a) \mbox{d} s(\bs{x}) + \simeq + \sum_{i=1}^{n_x} \sum_{j=1}^{n_y} G(x_i,y_j,z(x_i,y_i), x_a, y_a, z(x_a, y_a)) \, dx \, dy +\end{eqnarray} +% +Of course the value $z(x_a, y_a)$ is known, but not the function $z(x_i,y_i)$ +because is evaluated in points that are not reflected in the grid, so we must +compute this points from the available data. We will start renormalizing the +coordinates such that: +% +\begin{eqnarray} + \label{eq:laplace3D:integral:uv} + \begin{array}{lcl} + u & = & \dsty \frac{x - \left(x_a - Dx\right)}{2 Dx} + \\ + v & = & \dsty \frac{y - \left(y_a - Dy\right)}{2 Dy} + \end{array} +\end{eqnarray} +% +So we know the value of $z$ for all the combinations of $u = 0,0.5,1$ and +$v = 0,0.5,1$. Then, to can evaluate the function $z(u,v)$ for all $u,v$ +values we may to build a Spline surface with the data known from the 9 +points shown in the figure \ref{fig:ss:laplace3D:integral}. The Spline +surface can be characterized as +% +\begin{eqnarray} + \label{eq:laplace3D:integral:spline} + z(u,v) = k_0 + k_u u + k_v v + k_{uv} u v + k_{uu} u^2 + k_{vv} v^2 + + k_{uuv} u^2 v + k_{uvv} u v^2 + k_{uuvv} u^2 v^2 +\end{eqnarray} +% +In the equation \ref{eq:laplace3D:integral:spline} we have 9 unknown +coefficients, but we have available $z(u,v)$ for 9 points, so have 9 +unknowns with 9 equations that can be set as a linear system of +equations, that results in the following coefficients: +% +\begin{eqnarray} + \label{eq:laplace3D:integral:k} + \begin{array}{lcl} + k_0 & = & z \left( 0,0 \right) + \\ + k_u & = & - z \left( 1,0 \right) + 4 z \left( \frac{1}{2},0 \right) - 3 z \left( 0,0 \right) + \\ + k_v & = & - z \left( 0,1 \right) + 4 z \left( 0,\frac{1}{2} \right) - 3 z \left( 0,0 \right) + \\ + k_{uv} & = & z \left( 1,1 \right) - 4 z \left( 1,\frac{1}{2} \right) + 3 z \left( 1,0 \right) + - 4 z \left( \frac{1}{2},1 \right) + 16 z \left( \frac{1}{2},\frac{1}{2} \right) + \\ & & + - 12 z \left( \frac{1}{2},0 \right) + 3 z \left( 0,1 \right) + - 12 z \left( 0,\frac{1}{2} \right) + 9 z \left( 0,0 \right) + \\ + k_{uu} & = & 2 z \left( 1,0 \right) - 4 z \left( \frac{1}{2},0 \right) + 2 z \left( 1,0 \right) + \\ + k_{vv} & = & 2 z \left( 0,1 \right) - 4 z \left( 0,\frac{1}{2} \right) + 2 z \left( 1,0 \right) + \\ + k_{uuv} & = & - 2 z \left( 1,1 \right) + 8 z \left( 1,\frac{1}{2} \right) - 6 z \left( 1,0 \right) + + 4 z \left( \frac{1}{2},1 \right) - 16 z \left( \frac{1}{2},\frac{1}{2} \right) + \\ & & + + 12 z \left( \frac{1}{2},0 \right) - 2 z \left( 0,1 \right) + + 8 z \left( 0,\frac{1}{2} \right) - 6 z \left( 0,0 \right) + \\ + k_{uvv} & = & - 2 z \left( 1,1 \right) + 4 z \left( 1,\frac{1}{2} \right) - 2 z \left( 1,0 \right) + + 8 z \left( \frac{1}{2},1 \right) - 16 z \left( \frac{1}{2},\frac{1}{2} \right) + \\ & & + + 8 z \left( \frac{1}{2},0 \right) - 6 z \left( 0,1 \right) + + 12 z \left( 0,\frac{1}{2} \right) - 6 z \left( 0,0 \right) + \\ + k_{uuvv} & = & 4 z \left( 1,1 \right) - 8 z \left( 1,\frac{1}{2} \right) + 4 z \left( 1,0 \right) + - 8 z \left( \frac{1}{2},1 \right) + 16 z \left( \frac{1}{2},\frac{1}{2} \right) + \\ & & + - 8 z \left( \frac{1}{2},0 \right) + 4 z \left( 0,1 \right) + - 8 z \left( 0,\frac{1}{2} \right) + 4 z \left( 0,0 \right) + \end{array} +\end{eqnarray} +% +So using the equation \ref{eq:laplace3D:integral:g} to \ref{eq:laplace3D:integral:k} +we can compute the integrals in the equations \ref{eq:laplace3D:g:hat} and +\ref{eq:laplace3D:h:hat}. +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.6\textwidth]{Integral} + \caption{Integration method scheme} + \label{fig:ss:laplace3D:integral} +\end{figure} +% +\section{BEM test} +\label{ss:laplace3D:test} +% +\subsection{General} +\label{sss:laplace3D:test:general} +% +A Python script has been provided with this document in the subfolder \textbf{test}. +In the script all this theory is tested in order to know if the BEM is well purposed, +and the errors that can be expected from the method application.\rc +% +In the test, for the moment, only one wave will be set, and the computational free +surface will be big enough to contain 2 wave lengths. In this case a wave period +of $T = 2.5 s$ is used, resulting in a wave of 10 meters.\rc +% +In the figure \ref{fig:laplace3D:test:wave} the wave used, that runs in the x direction, +is shown. The free surface will be extended while $G(x,y,z,X,Y,Z) > 0.1$. +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.6\textwidth]{test_wave} + \caption{Wave used in the test} + \label{fig:laplace3D:test:wave} +\end{figure} +% +\subsection{Direct method} +\label{sss:laplace3D:test:direct} +% +The direct method consist in evaluate the velocity potential in several points +using the equation \ref{eq:laplace3D:reciprocal_relation:005}, testing the error +get.\rc +% +If we apply the direct method for all the points of the computational free surface, +we can compute the root mean square error as +% +\begin{eqnarray*} + RMS(nx,ny) = \sqrt{\frac{1}{nx \, ny} \sum_{i=1}^{nx} \sum_{j=1}^{ny} + \left( \phi_{direct}(x_i,y_j) - \phi(x_i,y_j) \right)^2 + } +\end{eqnarray*} +% +For $nx = 31$ and $ny = 15$ we have $RMS(31,15) = 0.08$. In the figure +\ref{fig:laplace3D:test:direct} the analytic velocity potential, and the +interpolated using the direct method, for a slice in the middle of the +free surface ($y=0$).\rc +% +The results quality is good, and can be improved increasing the number of +points in the computational free surface. +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.6\textwidth]{test_direct} + \caption{Direct method} + \label{fig:laplace3D:test:direct} +\end{figure} +% +\subsection{BEM} +\label{sss:laplace3D:test:bem} +% +In this case we want to apply the equation \ref{eq:laplace3D:reciprocal_relation:006}, +where a linear system of equations is purposed in order to compute the gradient of the +velocity potential, projected over the normal, for all the points of the grid. +% +For $nx = 31$ and $ny = 15$ we have $RMS(31,15) = 0.04$. In the figure +\ref{fig:laplace3D:test:bem} the analytic velocity potential, and the +interpolated using the direct method, for a slice in the middle of the +free surface ($y=0$).\rc +% +The results quality is nice like in the direct method. In order to get enough good +results at least 15 points per wave length must be used (like in this application). +% +\begin{figure}[ht!] + \centering + \includegraphics[width=0.6\textwidth]{test_bem} + \caption{BEM solution} + \label{fig:laplace3D:test:bem} +\end{figure} +% \ No newline at end of file diff --git a/src/Mod/Ship/simRun/theory/main.aux b/src/Mod/Ship/simRun/theory/main.aux new file mode 100644 index 000000000..e21f47e16 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.aux @@ -0,0 +1,125 @@ +\relax +\bibstyle{plainnat} +\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} +\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined +\global\let\oldcontentsline\contentsline +\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}} +\global\let\oldnewlabel\newlabel +\gdef\newlabel#1#2{\newlabelxx{#1}#2} +\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} +\AtEndDocument{\ifx\hyper@anchor\@undefined +\let\contentsline\oldcontentsline +\let\newlabel\oldnewlabel +\fi} +\fi} +\global\let\hyper@last\relax +\gdef\HyperFirstAtBeginDocument#1{#1} +\providecommand\HyField@AuxAddToFields[1]{} +\citation{bem_2007} +\citation{vinayan2007} +\@writefile{toc}{\contentsline {chapter}{\numberline {1}Introduction}{5}{chapter.1}} +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{s:introduction}{{1}{5}{Introduction\relax }{chapter.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {1.1}Objective}{5}{section.1.1}} +\citation{bem_2007} +\@writefile{toc}{\contentsline {chapter}{\numberline {2}Governing equations}{7}{chapter.2}} +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{s:governing_equations}{{2}{7}{Governing equations\relax }{chapter.2}{}} +\newlabel{eq:governing_equations:v_potential}{{2.2}{7}{Governing equations\relax }{equation.2.0.2}{}} +\newlabel{eq:governing_equations:laplace}{{2.3}{7}{Governing equations\relax }{equation.2.0.3}{}} +\newlabel{eq:governing_equations:bernoulli}{{2.4}{7}{Governing equations\relax }{equation.2.0.3}{}} +\@writefile{toc}{\contentsline {chapter}{\numberline {3}Waves propagations in 2D plane}{9}{chapter.3}} +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{s:laplace2D}{{3}{9}{Waves propagations in 2D plane\relax }{chapter.3}{}} +\@writefile{toc}{\contentsline {section}{\numberline {3.1}General}{9}{section.3.1}} +\@writefile{toc}{\contentsline {section}{\numberline {3.2}Incident waves}{9}{section.3.2}} +\newlabel{eq:laplace2D:incident_waves}{{3.1}{9}{Incident waves\relax }{equation.3.2.1}{}} +\newlabel{eq:laplace2D:c_general}{{3.2}{9}{Incident waves\relax }{equation.3.2.2}{}} +\newlabel{eq:laplace2D:c_deep}{{3.3}{9}{Incident waves\relax }{equation.3.2.3}{}} +\newlabel{eq:laplace2D:k_w}{{3.4}{9}{Incident waves\relax }{equation.3.2.4}{}} +\newlabel{eq:laplace2D:incident_waves_potential}{{3.5}{9}{Incident waves\relax }{equation.3.2.5}{}} +\@writefile{toc}{\contentsline {section}{\numberline {3.3}BEM applied to Laplace 2D problem}{10}{section.3.3}} +\newlabel{ss:laplace2D:bem}{{3.3}{10}{BEM applied to Laplace 2D problem\relax }{section.3.3}{}} +\newlabel{eq:laplace2D:reciprocal_relation}{{3.6}{10}{BEM applied to Laplace 2D problem\relax }{equation.3.3.6}{}} +\citation{vinayan2007} +\newlabel{eq:laplace2D:g}{{3.7}{11}{BEM applied to Laplace 2D problem\relax }{equation.3.3.7}{}} +\newlabel{eq:laplace2D:h}{{3.8}{11}{BEM applied to Laplace 2D problem\relax }{equation.3.3.7}{}} +\newlabel{eq:laplace2D:limit_g}{{3.9}{11}{BEM applied to Laplace 2D problem\relax }{equation.3.3.9}{}} +\newlabel{eq:laplace2D:limit_h}{{3.10}{11}{BEM applied to Laplace 2D problem\relax }{equation.3.3.9}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.1}{\ignorespaces Computational domain $\Omega $}}{11}{figure.3.1}} +\newlabel{fig:ss:laplace2D:bem}{{3.1}{11}{Computational domain $\Omega $\relax }{figure.3.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {3.4}Conclusions to Laplace 2D problem}{11}{section.3.4}} +\newlabel{ss:laplace2D:conclusions}{{3.4}{11}{Conclusions to Laplace 2D problem\relax }{section.3.4}{}} +\@writefile{toc}{\contentsline {chapter}{\numberline {4}Waves propagations in 3D}{13}{chapter.4}} +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{s:laplace3D}{{4}{13}{Waves propagations in 3D\relax }{chapter.4}{}} +\@writefile{toc}{\contentsline {section}{\numberline {4.1}General}{13}{section.4.1}} +\@writefile{toc}{\contentsline {section}{\numberline {4.2}Incident waves}{13}{section.4.2}} +\newlabel{eq:laplace3D:incident_waves}{{4.1}{13}{Incident waves\relax }{equation.4.2.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {4.3}BEM applied to Laplace 3D problem}{13}{section.4.3}} +\newlabel{ss:laplace3D:bem}{{4.3}{13}{BEM applied to Laplace 3D problem\relax }{section.4.3}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3.1}Computational domain}{13}{subsection.4.3.1}} +\newlabel{sss:laplace3D:computational_domain}{{4.3.1}{13}{Computational domain\relax }{subsection.4.3.1}{}} +\newlabel{eq:laplace3D:reciprocal_relation}{{4.2}{13}{Computational domain\relax }{equation.4.3.2}{}} +\citation{yang2004} +\newlabel{eq:laplace3D:g}{{4.3}{14}{Computational domain\relax }{equation.4.3.3}{}} +\newlabel{eq:laplace3D:h}{{4.4}{14}{Computational domain\relax }{equation.4.3.3}{}} +\newlabel{eq:laplace3D:limit_g}{{4.5}{14}{Computational domain\relax }{equation.4.3.5}{}} +\newlabel{eq:laplace3D:limit_h}{{4.6}{14}{Computational domain\relax }{equation.4.3.5}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4.1}{\ignorespaces Computational domain $\Omega $}}{14}{figure.4.1}} +\newlabel{fig:ss:laplace3D:bem}{{4.1}{14}{Computational domain $\Omega $\relax }{figure.4.1}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3.2}Boundary conditions (BC)}{14}{subsection.4.3.2}} +\newlabel{sss:laplace3D:BC}{{4.3.2}{14}{Boundary conditions (BC)\relax }{subsection.4.3.2}{}} +\newlabel{eq:laplace3D:FS_Bernoulli}{{4.7}{14}{Boundary conditions (BC)\relax }{equation.4.3.7}{}} +\newlabel{eq:laplace3D:DFSBC}{{4.8}{15}{Boundary conditions (BC)\relax }{equation.4.3.8}{}} +\newlabel{eq:laplace3D:FS_Kinematic}{{4.9}{15}{Boundary conditions (BC)\relax }{equation.4.3.9}{}} +\newlabel{eq:laplace3D:KFSBC}{{4.10}{15}{Boundary conditions (BC)\relax }{equation.4.3.10}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3.3}Time integration scheme}{15}{subsection.4.3.3}} +\newlabel{sss:laplace3D:TimeIntegration}{{4.3.3}{15}{Time integration scheme\relax }{subsection.4.3.3}{}} +\newlabel{eq:laplace3D:IC}{{4.11}{15}{Time integration scheme\relax }{equation.4.3.11}{}} +\newlabel{eq:laplace3D:time_march:bem}{{4.12}{15}{Time integration scheme\relax }{equation.4.3.12}{}} +\newlabel{eq:laplace3D:time_march:dzdt}{{4.13}{15}{Time integration scheme\relax }{equation.4.3.13}{}} +\newlabel{eq:laplace3D:time_march:dphidt}{{4.14}{15}{Time integration scheme\relax }{equation.4.3.13}{}} +\newlabel{eq:laplace3D:time_march:z_integrate}{{4.15}{15}{Time integration scheme\relax }{equation.4.3.15}{}} +\newlabel{eq:laplace3D:time_march:phi_integrate}{{4.16}{15}{Time integration scheme\relax }{equation.4.3.15}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3.4}Discrete Laplace solution using the BEM}{16}{subsection.4.3.4}} +\newlabel{sss:laplace3D:bem_solve}{{4.3.4}{16}{Discrete Laplace solution using the BEM\relax }{subsection.4.3.4}{}} +\newlabel{eq:laplace3D:reciprocal_relation:002}{{4.17}{16}{Discrete Laplace solution using the BEM\relax }{equation.4.3.17}{}} +\newlabel{eq:laplace3D:reciprocal_relation:003}{{4.18}{16}{Discrete Laplace solution using the BEM\relax }{equation.4.3.18}{}} +\newlabel{eq:laplace3D:reciprocal_relation:004}{{4.19}{16}{Discrete Laplace solution using the BEM\relax }{equation.4.3.19}{}} +\newlabel{eq:laplace3D:g:hat}{{4.20}{16}{Discrete Laplace solution using the BEM\relax }{equation.4.3.20}{}} +\newlabel{eq:laplace3D:h:hat}{{4.21}{16}{Discrete Laplace solution using the BEM\relax }{equation.4.3.20}{}} +\newlabel{eq:laplace3D:reciprocal_relation:005}{{4.22}{17}{Discrete Laplace solution using the BEM\relax }{equation.4.3.22}{}} +\newlabel{eq:laplace3D:reciprocal_relation:006}{{4.23}{17}{Discrete Laplace solution using the BEM\relax }{equation.4.3.23}{}} +\newlabel{eq:laplace3D:reciprocal_relation:007}{{4.24}{17}{Discrete Laplace solution using the BEM\relax }{equation.4.3.24}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3.5}Integrals computation}{17}{subsection.4.3.5}} +\newlabel{sss:laplace3D:integrals}{{4.3.5}{17}{Integrals computation\relax }{subsection.4.3.5}{}} +\newlabel{eq:laplace3D:integral:g}{{4.25}{17}{Integrals computation\relax }{equation.4.3.25}{}} +\newlabel{eq:laplace3D:integral:uv}{{4.26}{18}{Integrals computation\relax }{equation.4.3.26}{}} +\newlabel{eq:laplace3D:integral:spline}{{4.27}{18}{Integrals computation\relax }{equation.4.3.27}{}} +\newlabel{eq:laplace3D:integral:k}{{4.28}{18}{Integrals computation\relax }{equation.4.3.28}{}} +\@writefile{toc}{\contentsline {section}{\numberline {4.4}BEM test}{18}{section.4.4}} +\newlabel{ss:laplace3D:test}{{4.4}{18}{BEM test\relax }{section.4.4}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.1}General}{18}{subsection.4.4.1}} +\newlabel{sss:laplace3D:test:general}{{4.4.1}{18}{General\relax }{subsection.4.4.1}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4.2}{\ignorespaces Integration method scheme}}{19}{figure.4.2}} +\newlabel{fig:ss:laplace3D:integral}{{4.2}{19}{Integration method scheme\relax }{figure.4.2}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4.3}{\ignorespaces Wave used in the test}}{19}{figure.4.3}} +\newlabel{fig:laplace3D:test:wave}{{4.3}{19}{Wave used in the test\relax }{figure.4.3}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.2}Direct method}{19}{subsection.4.4.2}} +\newlabel{sss:laplace3D:test:direct}{{4.4.2}{19}{Direct method\relax }{subsection.4.4.2}{}} +\bibdata{bib} +\@writefile{lof}{\contentsline {figure}{\numberline {4.4}{\ignorespaces Direct method}}{20}{figure.4.4}} +\newlabel{fig:laplace3D:test:direct}{{4.4}{20}{Direct method\relax }{figure.4.4}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.3}BEM}{20}{subsection.4.4.3}} +\newlabel{sss:laplace3D:test:bem}{{4.4.3}{20}{BEM\relax }{subsection.4.4.3}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4.5}{\ignorespaces BEM solution}}{21}{figure.4.5}} +\newlabel{fig:laplace3D:test:bem}{{4.5}{21}{BEM solution\relax }{figure.4.5}{}} +\bibcite{bem_2007}{{1}{2007}{{Ang}}{{}}} +\bibcite{vinayan2007}{{2}{2007}{{Vinayan and Kinnas}}{{}}} +\bibcite{yang2004}{{3}{2004}{{Yang}}{{}}} +\@writefile{toc}{\contentsline {chapter}{Bibliography}{23}{chapter*.2}} diff --git a/src/Mod/Ship/simRun/theory/main.bbl b/src/Mod/Ship/simRun/theory/main.bbl new file mode 100644 index 000000000..501e72326 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.bbl @@ -0,0 +1,24 @@ +\begin{thebibliography}{3} +\providecommand{\natexlab}[1]{#1} +\providecommand{\url}[1]{\texttt{#1}} +\expandafter\ifx\csname urlstyle\endcsname\relax + \providecommand{\doi}[1]{doi: #1}\else + \providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi + +\bibitem[Ang(2007)]{bem_2007} +Whye-Teong Ang. +\newblock \emph{A Beginner’s Course in Boundary Element Methods}. +\newblock Cambridge University Press, New York, 2007. + +\bibitem[Vinayan and Kinnas(2007)]{vinayan2007} +V.~Vinayan and S.~A. Kinnas. +\newblock A bem for the propagation of nonlinear planar free-surface waves. +\newblock \emph{Electronic Journal of Boundary Elements}, 5:\penalty0 17--40, + 2007. + +\bibitem[Yang(2004)]{yang2004} +Jinghai Yang. +\newblock \emph{Time domain, nonlinear theories on ship motions}. +\newblock PhD thesis, University of Hawaii, 2004. + +\end{thebibliography} diff --git a/src/Mod/Ship/simRun/theory/main.blg b/src/Mod/Ship/simRun/theory/main.blg new file mode 100644 index 000000000..72657ecfe --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.blg @@ -0,0 +1,48 @@ +This is BibTeX, Version 0.99d (TeX Live 2012/Debian) +Capacity: max_strings=35307, hash_size=35307, hash_prime=30011 +The top-level auxiliary file: main.aux +The style file: plainnat.bst +Database file #1: bib.bib +Warning--can't use both author and editor fields in bem_2007 +You've used 3 entries, + 2773 wiz_defined-function locations, + 726 strings with 6674 characters, +and the built_in function-call counts, 1030 in all, are: += -- 88 +> -- 41 +< -- 3 ++ -- 15 +- -- 12 +* -- 67 +:= -- 173 +add.period$ -- 9 +call.type$ -- 3 +change.case$ -- 11 +chr.to.int$ -- 3 +cite$ -- 7 +duplicate$ -- 56 +empty$ -- 92 +format.name$ -- 17 +if$ -- 207 +int.to.chr$ -- 1 +int.to.str$ -- 1 +missing$ -- 3 +newline$ -- 23 +num.names$ -- 12 +pop$ -- 25 +preamble$ -- 1 +purify$ -- 10 +quote$ -- 0 +skip$ -- 41 +stack$ -- 0 +substring$ -- 21 +swap$ -- 4 +text.length$ -- 0 +text.prefix$ -- 0 +top$ -- 0 +type$ -- 28 +warning$ -- 1 +while$ -- 10 +width$ -- 0 +write$ -- 45 +(There was 1 warning) diff --git a/src/Mod/Ship/simRun/theory/main.log b/src/Mod/Ship/simRun/theory/main.log new file mode 100644 index 000000000..a224a19a6 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.log @@ -0,0 +1,827 @@ +This is pdfTeX, Version 3.1415926-2.4-1.40.13 (TeX Live 2012/Debian) (format=pdflatex 2012.12.24) 1 APR 2013 19:55 +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**main.tex +(./main.tex +LaTeX2e <2011/06/27> +Babel and hyphenation patterns for english, dumylang, nohyphenation, lo +aded. +(/usr/share/texlive/texmf-dist/tex/latex/base/book.cls +Document Class: book 2007/10/19 v1.4h Standard LaTeX document class +(/usr/share/texlive/texmf-dist/tex/latex/base/bk12.clo +File: bk12.clo 2007/10/19 v1.4h Standard LaTeX file (size option) +) +\c@part=\count79 +\c@chapter=\count80 +\c@section=\count81 +\c@subsection=\count82 +\c@subsubsection=\count83 +\c@paragraph=\count84 +\c@subparagraph=\count85 +\c@figure=\count86 +\c@table=\count87 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +\bibindent=\dimen102 +) +(/usr/share/texlive/texmf-dist/tex/latex/natbib/natbib.sty +Package: natbib 2010/09/13 8.31b (PWD, AO) +\bibhang=\skip43 +\bibsep=\skip44 +LaTeX Info: Redefining \cite on input line 694. +\c@NAT@ctr=\count88 +) +(/usr/share/texlive/texmf-dist/tex/latex/graphics/graphicx.sty +Package: graphicx 1999/02/16 v1.0f Enhanced LaTeX Graphics (DPC,SPQR) + +(/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 1999/03/16 v1.13 key=value parser (DPC) +\KV@toks@=\toks14 +) +(/usr/share/texlive/texmf-dist/tex/latex/graphics/graphics.sty +Package: graphics 2009/02/05 v1.0o Standard LaTeX Graphics (DPC,SPQR) + +(/usr/share/texlive/texmf-dist/tex/latex/graphics/trig.sty +Package: trig 1999/03/16 v1.09 sin cos tan (DPC) +) +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/graphics.cfg +File: graphics.cfg 2010/04/23 v1.9 graphics configuration of TeX Live +) +Package graphics Info: Driver file: pdftex.def on input line 91. + +(/usr/share/texlive/texmf-dist/tex/latex/pdftex-def/pdftex.def +File: pdftex.def 2011/05/27 v0.06d Graphics/color for pdfTeX + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/infwarerr.sty +Package: infwarerr 2010/04/08 v1.3 Providing info/warning/error messages (HO) +) +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/ltxcmds.sty +Package: ltxcmds 2011/11/09 v1.22 LaTeX kernel commands for general use (HO) +) +\Gread@gobject=\count89 +)) +\Gin@req@height=\dimen103 +\Gin@req@width=\dimen104 +) +(/usr/share/texlive/texmf-dist/tex/latex/tools/verbatim.sty +Package: verbatim 2003/08/22 v1.5q LaTeX2e package for verbatim enhancements +\every@verbatim=\toks15 +\verbatim@line=\toks16 +\verbatim@in@stream=\read1 +) +(/usr/share/texlive/texmf-dist/tex/latex/graphics/color.sty +Package: color 2005/11/14 v1.0j Standard LaTeX Color (DPC) + +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/color.cfg +File: color.cfg 2007/01/18 v1.5 color configuration of teTeX/TeXLive +) +Package color Info: Driver file: pdftex.def on input line 130. +) +(/usr/share/texlive/texmf-dist/tex/latex/colortbl/colortbl.sty +Package: colortbl 2012/02/13 v1.0a Color table columns (DPC) + +(/usr/share/texlive/texmf-dist/tex/latex/tools/array.sty +Package: array 2008/09/09 v2.4c Tabular extension package (FMi) +\col@sep=\dimen105 +\extrarowheight=\dimen106 +\NC@list=\toks17 +\extratabsurround=\skip45 +\backup@length=\skip46 +) +\everycr=\toks18 +\minrowclearance=\skip47 +) +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/hyperref.sty +Package: hyperref 2012/05/13 v6.82q Hypertext links for LaTeX + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-hyperref.sty +Package: hobsub-hyperref 2012/05/28 v1.13 Bundle oberdiek, subset hyperref (HO) + + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/hobsub-generic.sty +Package: hobsub-generic 2012/05/28 v1.13 Bundle oberdiek, subset generic (HO) +Package: hobsub 2012/05/28 v1.13 Construct package bundles (HO) +Package hobsub Info: Skipping package `infwarerr' (already loaded). +Package hobsub Info: Skipping package `ltxcmds' (already loaded). +Package: ifluatex 2010/03/01 v1.3 Provides the ifluatex switch (HO) +Package ifluatex Info: LuaTeX not detected. +Package: ifvtex 2010/03/01 v1.5 Detect VTeX and its facilities (HO) +Package ifvtex Info: VTeX not detected. +Package: intcalc 2007/09/27 v1.1 Expandable calculations with integers (HO) +Package: ifpdf 2011/01/30 v2.3 Provides the ifpdf switch (HO) +Package ifpdf Info: pdfTeX in PDF mode is detected. +Package: etexcmds 2011/02/16 v1.5 Avoid name clashes with e-TeX commands (HO) +Package etexcmds Info: Could not find \expanded. +(etexcmds) That can mean that you are not using pdfTeX 1.50 or +(etexcmds) that some package has redefined \expanded. +(etexcmds) In the latter case, load this package earlier. +Package: kvsetkeys 2012/04/25 v1.16 Key value parser (HO) +Package: kvdefinekeys 2011/04/07 v1.3 Define keys (HO) +Package: pdftexcmds 2011/11/29 v0.20 Utility functions of pdfTeX for LuaTeX (HO +) +Package pdftexcmds Info: LuaTeX not detected. +Package pdftexcmds Info: \pdf@primitive is available. +Package pdftexcmds Info: \pdf@ifprimitive is available. +Package pdftexcmds Info: \pdfdraftmode found. +Package: pdfescape 2011/11/25 v1.13 Implements pdfTeX's escape features (HO) +Package: bigintcalc 2012/04/08 v1.3 Expandable calculations on big integers (HO +) +Package: bitset 2011/01/30 v1.1 Handle bit-vector datatype (HO) +Package: uniquecounter 2011/01/30 v1.2 Provide unlimited unique counter (HO) +) +Package hobsub Info: Skipping package `hobsub' (already loaded). +Package: letltxmacro 2010/09/02 v1.4 Let assignment for LaTeX macros (HO) +Package: hopatch 2012/05/28 v1.2 Wrapper for package hooks (HO) +Package: xcolor-patch 2011/01/30 xcolor patch +Package: atveryend 2011/06/30 v1.8 Hooks at the very end of document (HO) +Package atveryend Info: \enddocument detected (standard20110627). +Package: atbegshi 2011/10/05 v1.16 At begin shipout hook (HO) +Package: refcount 2011/10/16 v3.4 Data extraction from label references (HO) +Package: hycolor 2011/01/30 v1.7 Color options for hyperref/bookmark (HO) +) +(/usr/share/texlive/texmf-dist/tex/generic/ifxetex/ifxetex.sty +Package: ifxetex 2010/09/12 v0.6 Provides ifxetex conditional +) +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/kvoptions.sty +Package: kvoptions 2011/06/30 v3.11 Key value format for package options (HO) +) +\@linkdim=\dimen107 +\Hy@linkcounter=\count90 +\Hy@pagecounter=\count91 + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/pd1enc.def +File: pd1enc.def 2012/05/13 v6.82q Hyperref: PDFDocEncoding definition (HO) +) +\Hy@SavedSpaceFactor=\count92 + +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/hyperref.cfg +File: hyperref.cfg 2002/06/06 v1.2 hyperref configuration of TeXLive +) +Package hyperref Info: Hyper figures OFF on input line 4062. +Package hyperref Info: Link nesting OFF on input line 4067. +Package hyperref Info: Hyper index ON on input line 4070. +Package hyperref Info: Plain pages OFF on input line 4077. +Package hyperref Info: Backreferencing OFF on input line 4082. +Package hyperref Info: Implicit mode ON; LaTeX internals redefined. +Package hyperref Info: Bookmarks ON on input line 4300. +\c@Hy@tempcnt=\count93 + +(/usr/share/texlive/texmf-dist/tex/latex/url/url.sty +\Urlmuskip=\muskip10 +Package: url 2006/04/12 ver 3.3 Verb mode for urls, etc. +) +LaTeX Info: Redefining \url on input line 4653. +\Fld@menulength=\count94 +\Field@Width=\dimen108 +\Fld@charsize=\dimen109 +Package hyperref Info: Hyper figures OFF on input line 5773. +Package hyperref Info: Link nesting OFF on input line 5778. +Package hyperref Info: Hyper index ON on input line 5781. +Package hyperref Info: backreferencing OFF on input line 5788. +Package hyperref Info: Link coloring OFF on input line 5793. +Package hyperref Info: Link coloring with OCG OFF on input line 5798. +Package hyperref Info: PDF/A mode OFF on input line 5803. +LaTeX Info: Redefining \ref on input line 5843. +LaTeX Info: Redefining \pageref on input line 5847. +\Hy@abspage=\count95 +\c@Item=\count96 +\c@Hfootnote=\count97 +) + +Package hyperref Message: Driver (autodetected): hpdftex. + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/hpdftex.def +File: hpdftex.def 2012/05/13 v6.82q Hyperref driver for pdfTeX +\Fld@listcount=\count98 +\c@bookmark@seq@number=\count99 + +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/rerunfilecheck.sty +Package: rerunfilecheck 2011/04/15 v1.7 Rerun checks for auxiliary files (HO) +Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 2 +82. +) +\Hy@SectionHShift=\skip48 +) +(/usr/share/texlive/texmf-dist/tex/latex/base/inputenc.sty +Package: inputenc 2008/03/30 v1.1d Input encoding file +\inpenc@prehook=\toks19 +\inpenc@posthook=\toks20 + +(/usr/share/texlive/texmf-dist/tex/latex/base/utf8.def +File: utf8.def 2008/04/05 v1.1m UTF-8 support for inputenc +Now handling font encoding OML ... +... no UTF-8 mapping file for font encoding OML +Now handling font encoding T1 ... +... processing UTF-8 mapping file for font encoding T1 + +(/usr/share/texlive/texmf-dist/tex/latex/base/t1enc.dfu +File: t1enc.dfu 2008/04/05 v1.1m UTF-8 support for inputenc + defining Unicode char U+00A1 (decimal 161) + defining Unicode char U+00A3 (decimal 163) + defining Unicode char U+00AB (decimal 171) + defining Unicode char U+00BB (decimal 187) + defining Unicode char U+00BF (decimal 191) + defining Unicode char U+00C0 (decimal 192) + defining Unicode char U+00C1 (decimal 193) + defining Unicode char U+00C2 (decimal 194) + defining Unicode char U+00C3 (decimal 195) + defining Unicode char U+00C4 (decimal 196) + defining Unicode char U+00C5 (decimal 197) + defining Unicode char U+00C6 (decimal 198) + defining Unicode char U+00C7 (decimal 199) + defining Unicode char U+00C8 (decimal 200) + defining Unicode char U+00C9 (decimal 201) + defining Unicode char U+00CA (decimal 202) + defining Unicode char U+00CB (decimal 203) + defining Unicode char U+00CC (decimal 204) + defining Unicode char U+00CD (decimal 205) + defining Unicode char U+00CE (decimal 206) + defining Unicode char U+00CF (decimal 207) + defining Unicode char U+00D0 (decimal 208) + defining Unicode char U+00D1 (decimal 209) + defining Unicode char U+00D2 (decimal 210) + defining Unicode char U+00D3 (decimal 211) + defining Unicode char U+00D4 (decimal 212) + defining Unicode char U+00D5 (decimal 213) + defining Unicode char U+00D6 (decimal 214) + defining Unicode char U+00D8 (decimal 216) + defining Unicode char U+00D9 (decimal 217) + defining Unicode char U+00DA (decimal 218) + defining Unicode char U+00DB (decimal 219) + defining Unicode char U+00DC (decimal 220) + defining Unicode char U+00DD (decimal 221) + defining Unicode char U+00DE (decimal 222) + defining Unicode char U+00DF (decimal 223) + defining Unicode char U+00E0 (decimal 224) + defining Unicode char U+00E1 (decimal 225) + defining Unicode char U+00E2 (decimal 226) + defining Unicode char U+00E3 (decimal 227) + defining Unicode char U+00E4 (decimal 228) + defining Unicode char U+00E5 (decimal 229) + defining Unicode char U+00E6 (decimal 230) + defining Unicode char U+00E7 (decimal 231) + defining Unicode char U+00E8 (decimal 232) + defining Unicode char U+00E9 (decimal 233) + defining Unicode char U+00EA (decimal 234) + defining Unicode char U+00EB (decimal 235) + defining Unicode char U+00EC (decimal 236) + defining Unicode char U+00ED (decimal 237) + defining Unicode char U+00EE (decimal 238) + defining Unicode char U+00EF (decimal 239) + defining Unicode char U+00F0 (decimal 240) + defining Unicode char U+00F1 (decimal 241) + defining Unicode char U+00F2 (decimal 242) + defining Unicode char U+00F3 (decimal 243) + defining Unicode char U+00F4 (decimal 244) + defining Unicode char U+00F5 (decimal 245) + defining Unicode char U+00F6 (decimal 246) + defining Unicode char U+00F8 (decimal 248) + defining Unicode char U+00F9 (decimal 249) + defining Unicode char U+00FA (decimal 250) + defining Unicode char U+00FB (decimal 251) + defining Unicode char U+00FC (decimal 252) + defining Unicode char U+00FD (decimal 253) + defining Unicode char U+00FE (decimal 254) + defining Unicode char U+00FF (decimal 255) + defining Unicode char U+0102 (decimal 258) + defining Unicode char U+0103 (decimal 259) + defining Unicode char U+0104 (decimal 260) + defining Unicode char U+0105 (decimal 261) + defining Unicode char U+0106 (decimal 262) + defining Unicode char U+0107 (decimal 263) + defining Unicode char U+010C (decimal 268) + defining Unicode char U+010D (decimal 269) + defining Unicode char U+010E (decimal 270) + defining Unicode char U+010F (decimal 271) + defining Unicode char U+0110 (decimal 272) + defining Unicode char U+0111 (decimal 273) + defining Unicode char U+0118 (decimal 280) + defining Unicode char U+0119 (decimal 281) + defining Unicode char U+011A (decimal 282) + defining Unicode char U+011B (decimal 283) + defining Unicode char U+011E (decimal 286) + defining Unicode char U+011F (decimal 287) + defining Unicode char U+0130 (decimal 304) + defining Unicode char U+0131 (decimal 305) + defining Unicode char U+0132 (decimal 306) + defining Unicode char U+0133 (decimal 307) + defining Unicode char U+0139 (decimal 313) + defining Unicode char U+013A (decimal 314) + defining Unicode char U+013D (decimal 317) + defining Unicode char U+013E (decimal 318) + defining Unicode char U+0141 (decimal 321) + defining Unicode char U+0142 (decimal 322) + defining Unicode char U+0143 (decimal 323) + defining Unicode char U+0144 (decimal 324) + defining Unicode char U+0147 (decimal 327) + defining Unicode char U+0148 (decimal 328) + defining Unicode char U+014A (decimal 330) + defining Unicode char U+014B (decimal 331) + defining Unicode char U+0150 (decimal 336) + defining Unicode char U+0151 (decimal 337) + defining Unicode char U+0152 (decimal 338) + defining Unicode char U+0153 (decimal 339) + defining Unicode char U+0154 (decimal 340) + defining Unicode char U+0155 (decimal 341) + defining Unicode char U+0158 (decimal 344) + defining Unicode char U+0159 (decimal 345) + defining Unicode char U+015A (decimal 346) + defining Unicode char U+015B (decimal 347) + defining Unicode char U+015E (decimal 350) + defining Unicode char U+015F (decimal 351) + defining Unicode char U+0160 (decimal 352) + defining Unicode char U+0161 (decimal 353) + defining Unicode char U+0162 (decimal 354) + defining Unicode char U+0163 (decimal 355) + defining Unicode char U+0164 (decimal 356) + defining Unicode char U+0165 (decimal 357) + defining Unicode char U+016E (decimal 366) + defining Unicode char U+016F (decimal 367) + defining Unicode char U+0170 (decimal 368) + defining Unicode char U+0171 (decimal 369) + defining Unicode char U+0178 (decimal 376) + defining Unicode char U+0179 (decimal 377) + defining Unicode char U+017A (decimal 378) + defining Unicode char U+017B (decimal 379) + defining Unicode char U+017C (decimal 380) + defining Unicode char U+017D (decimal 381) + defining Unicode char U+017E (decimal 382) + defining Unicode char U+200C (decimal 8204) + defining Unicode char U+2013 (decimal 8211) + defining Unicode char U+2014 (decimal 8212) + defining Unicode char U+2018 (decimal 8216) + defining Unicode char U+2019 (decimal 8217) + defining Unicode char U+201A (decimal 8218) + defining Unicode char U+201C (decimal 8220) + defining Unicode char U+201D (decimal 8221) + defining Unicode char U+201E (decimal 8222) + defining Unicode char U+2030 (decimal 8240) + defining Unicode char U+2031 (decimal 8241) + defining Unicode char U+2039 (decimal 8249) + defining Unicode char U+203A (decimal 8250) + defining Unicode char U+2423 (decimal 9251) +) +Now handling font encoding OT1 ... +... processing UTF-8 mapping file for font encoding OT1 + +(/usr/share/texlive/texmf-dist/tex/latex/base/ot1enc.dfu +File: ot1enc.dfu 2008/04/05 v1.1m UTF-8 support for inputenc + defining Unicode char U+00A1 (decimal 161) + defining Unicode char U+00A3 (decimal 163) + defining Unicode char U+00B8 (decimal 184) + defining Unicode char U+00BF (decimal 191) + defining Unicode char U+00C5 (decimal 197) + defining Unicode char U+00C6 (decimal 198) + defining Unicode char U+00D8 (decimal 216) + defining Unicode char U+00DF (decimal 223) + defining Unicode char U+00E6 (decimal 230) + defining Unicode char U+00EC (decimal 236) + defining Unicode char U+00ED (decimal 237) + defining Unicode char U+00EE (decimal 238) + defining Unicode char U+00EF (decimal 239) + defining Unicode char U+00F8 (decimal 248) + defining Unicode char U+0131 (decimal 305) + defining Unicode char U+0141 (decimal 321) + defining Unicode char U+0142 (decimal 322) + defining Unicode char U+0152 (decimal 338) + defining Unicode char U+0153 (decimal 339) + defining Unicode char U+2013 (decimal 8211) + defining Unicode char U+2014 (decimal 8212) + defining Unicode char U+2018 (decimal 8216) + defining Unicode char U+2019 (decimal 8217) + defining Unicode char U+201C (decimal 8220) + defining Unicode char U+201D (decimal 8221) +) +Now handling font encoding OMS ... +... processing UTF-8 mapping file for font encoding OMS + +(/usr/share/texlive/texmf-dist/tex/latex/base/omsenc.dfu +File: omsenc.dfu 2008/04/05 v1.1m UTF-8 support for inputenc + defining Unicode char U+00A7 (decimal 167) + defining Unicode char U+00B6 (decimal 182) + defining Unicode char U+00B7 (decimal 183) + defining Unicode char U+2020 (decimal 8224) + defining Unicode char U+2021 (decimal 8225) + defining Unicode char U+2022 (decimal 8226) +) +Now handling font encoding OMX ... +... no UTF-8 mapping file for font encoding OMX +Now handling font encoding U ... +... no UTF-8 mapping file for font encoding U +Now handling font encoding PD1 ... +... no UTF-8 mapping file for font encoding PD1 + defining Unicode char U+00A9 (decimal 169) + defining Unicode char U+00AA (decimal 170) + defining Unicode char U+00AE (decimal 174) + defining Unicode char U+00BA (decimal 186) + defining Unicode char U+02C6 (decimal 710) + defining Unicode char U+02DC (decimal 732) + defining Unicode char U+200C (decimal 8204) + defining Unicode char U+2026 (decimal 8230) + defining Unicode char U+2122 (decimal 8482) + defining Unicode char U+2423 (decimal 9251) +)) +(/usr/share/texlive/texmf-dist/tex/latex/base/alltt.sty +Package: alltt 1997/06/16 v2.0g defines alltt environment +) +(/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty +Package: amsmath 2000/07/18 v2.13 AMS math features +\@mathmargin=\skip49 + +For additional information on amsmath, use the `?' option. +(/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty +Package: amstext 2000/06/29 v2.01 + +(/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty +File: amsgen.sty 1999/11/30 v2.0 +\@emptytoks=\toks21 +\ex@=\dimen110 +)) +(/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty +Package: amsbsy 1999/11/29 v1.2d +\pmbraise@=\dimen111 +) +(/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty +Package: amsopn 1999/12/14 v2.01 operator names +) +\inf@bad=\count100 +LaTeX Info: Redefining \frac on input line 211. +\uproot@=\count101 +\leftroot@=\count102 +LaTeX Info: Redefining \overline on input line 307. +\classnum@=\count103 +\DOTSCASE@=\count104 +LaTeX Info: Redefining \ldots on input line 379. +LaTeX Info: Redefining \dots on input line 382. +LaTeX Info: Redefining \cdots on input line 467. +\Mathstrutbox@=\box26 +\strutbox@=\box27 +\big@size=\dimen112 +LaTeX Font Info: Redeclaring font encoding OML on input line 567. +LaTeX Font Info: Redeclaring font encoding OMS on input line 568. +\macc@depth=\count105 +\c@MaxMatrixCols=\count106 +\dotsspace@=\muskip11 +\c@parentequation=\count107 +\dspbrk@lvl=\count108 +\tag@help=\toks22 +\row@=\count109 +\column@=\count110 +\maxfields@=\count111 +\andhelp@=\toks23 +\eqnshift@=\dimen113 +\alignsep@=\dimen114 +\tagshift@=\dimen115 +\tagwidth@=\dimen116 +\totwidth@=\dimen117 +\lineht@=\dimen118 +\@envbody=\toks24 +\multlinegap=\skip50 +\multlinetaggap=\skip51 +\mathdisplay@stack=\toks25 +LaTeX Info: Redefining \[ on input line 2666. +LaTeX Info: Redefining \] on input line 2667. +) +(/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty +Package: amssymb 2009/06/22 v3.00 + +(/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty +Package: amsfonts 2009/06/22 v3.00 Basic AMSFonts support +\symAMSa=\mathgroup4 +\symAMSb=\mathgroup5 +LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' +(Font) U/euf/m/n --> U/euf/b/n on input line 96. +)) +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/txfonts.sty +Package: txfonts 2008/01/22 v3.2.1 +LaTeX Font Info: Redeclaring symbol font `operators' on input line 21. +LaTeX Font Info: Overwriting symbol font `operators' in version `normal' +(Font) OT1/cmr/m/n --> OT1/txr/m/n on input line 21. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) OT1/cmr/bx/n --> OT1/txr/m/n on input line 21. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) OT1/txr/m/n --> OT1/txr/bx/n on input line 22. +\symitalic=\mathgroup6 +LaTeX Font Info: Overwriting symbol font `italic' in version `bold' +(Font) OT1/txr/m/it --> OT1/txr/bx/it on input line 26. +LaTeX Font Info: Redeclaring math alphabet \mathbf on input line 29. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' +(Font) OT1/cmr/bx/n --> OT1/txr/bx/n on input line 29. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' +(Font) OT1/cmr/bx/n --> OT1/txr/bx/n on input line 29. +LaTeX Font Info: Redeclaring math alphabet \mathit on input line 30. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' +(Font) OT1/cmr/m/it --> OT1/txr/m/it on input line 30. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) OT1/cmr/bx/it --> OT1/txr/m/it on input line 30. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) OT1/txr/m/it --> OT1/txr/bx/it on input line 31. +LaTeX Font Info: Redeclaring math alphabet \mathsf on input line 40. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' +(Font) OT1/cmss/m/n --> OT1/txss/m/n on input line 40. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) OT1/cmss/bx/n --> OT1/txss/m/n on input line 40. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) OT1/txss/m/n --> OT1/txss/b/n on input line 41. +LaTeX Font Info: Redeclaring math alphabet \mathtt on input line 50. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' +(Font) OT1/cmtt/m/n --> OT1/txtt/m/n on input line 50. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) OT1/cmtt/m/n --> OT1/txtt/m/n on input line 50. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) OT1/txtt/m/n --> OT1/txtt/b/n on input line 51. +LaTeX Font Info: Redeclaring symbol font `letters' on input line 58. +LaTeX Font Info: Overwriting symbol font `letters' in version `normal' +(Font) OML/cmm/m/it --> OML/txmi/m/it on input line 58. +LaTeX Font Info: Overwriting symbol font `letters' in version `bold' +(Font) OML/cmm/b/it --> OML/txmi/m/it on input line 58. +LaTeX Font Info: Overwriting symbol font `letters' in version `bold' +(Font) OML/txmi/m/it --> OML/txmi/bx/it on input line 59. +\symlettersA=\mathgroup7 +LaTeX Font Info: Overwriting symbol font `lettersA' in version `bold' +(Font) U/txmia/m/it --> U/txmia/bx/it on input line 67. +LaTeX Font Info: Redeclaring math alphabet \mathfrak on input line 70. +LaTeX Font Info: Redeclaring symbol font `symbols' on input line 77. +LaTeX Font Info: Overwriting symbol font `symbols' in version `normal' +(Font) OMS/cmsy/m/n --> OMS/txsy/m/n on input line 77. +LaTeX Font Info: Overwriting symbol font `symbols' in version `bold' +(Font) OMS/cmsy/b/n --> OMS/txsy/m/n on input line 77. +LaTeX Font Info: Overwriting symbol font `symbols' in version `bold' +(Font) OMS/txsy/m/n --> OMS/txsy/bx/n on input line 78. +LaTeX Font Info: Redeclaring symbol font `AMSa' on input line 93. +LaTeX Font Info: Overwriting symbol font `AMSa' in version `normal' +(Font) U/msa/m/n --> U/txsya/m/n on input line 93. +LaTeX Font Info: Overwriting symbol font `AMSa' in version `bold' +(Font) U/msa/m/n --> U/txsya/m/n on input line 93. +LaTeX Font Info: Overwriting symbol font `AMSa' in version `bold' +(Font) U/txsya/m/n --> U/txsya/bx/n on input line 94. +LaTeX Font Info: Redeclaring symbol font `AMSb' on input line 102. +LaTeX Font Info: Overwriting symbol font `AMSb' in version `normal' +(Font) U/msb/m/n --> U/txsyb/m/n on input line 102. +LaTeX Font Info: Overwriting symbol font `AMSb' in version `bold' +(Font) U/msb/m/n --> U/txsyb/m/n on input line 102. +LaTeX Font Info: Overwriting symbol font `AMSb' in version `bold' +(Font) U/txsyb/m/n --> U/txsyb/bx/n on input line 103. +\symsymbolsC=\mathgroup8 +LaTeX Font Info: Overwriting symbol font `symbolsC' in version `bold' +(Font) U/txsyc/m/n --> U/txsyc/bx/n on input line 113. +LaTeX Font Info: Redeclaring symbol font `largesymbols' on input line 120. +LaTeX Font Info: Overwriting symbol font `largesymbols' in version `normal' +(Font) OMX/cmex/m/n --> OMX/txex/m/n on input line 120. +LaTeX Font Info: Overwriting symbol font `largesymbols' in version `bold' +(Font) OMX/cmex/m/n --> OMX/txex/m/n on input line 120. +LaTeX Font Info: Overwriting symbol font `largesymbols' in version `bold' +(Font) OMX/txex/m/n --> OMX/txex/bx/n on input line 121. +\symlargesymbolsA=\mathgroup9 +LaTeX Font Info: Overwriting symbol font `largesymbolsA' in version `bold' +(Font) U/txexa/m/n --> U/txexa/bx/n on input line 129. +LaTeX Info: Redefining \not on input line 1043. +) +(/usr/share/texlive/texmf-dist/tex/latex/anysize/anysize.sty +Package: anysize 1994/08/13 setting margin sizes + +document style option `anysize' loaded +Michael Salzenberg, Thomas Esser, Dirk Hillbrecht +Version 1.0, Aug 13, 1994 +\@Leftmargin=\dimen119 +\@Rightmargin=\dimen120 +\@Topmargin=\dimen121 +\@Bottommargin=\dimen122 +) (/usr/share/texlive/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty +\fancy@headwidth=\skip52 +\f@ncyO@elh=\skip53 +\f@ncyO@erh=\skip54 +\f@ncyO@olh=\skip55 +\f@ncyO@orh=\skip56 +\f@ncyO@elf=\skip57 +\f@ncyO@erf=\skip58 +\f@ncyO@olf=\skip59 +\f@ncyO@orf=\skip60 +) (./main.aux) +\openout1 = `main.aux'. + +LaTeX Font Info: Checking defaults for OML/txmi/m/it on input line 58. +LaTeX Font Info: Try loading font information for OML+txmi on input line 58. + + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/omltxmi.fd +File: omltxmi.fd 2000/12/15 v3.1 +) +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 58. +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 58. +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for OMS/txsy/m/n on input line 58. +LaTeX Font Info: Try loading font information for OMS+txsy on input line 58. + + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/omstxsy.fd +File: omstxsy.fd 2000/12/15 v3.1 +) +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for OMX/txex/m/n on input line 58. +LaTeX Font Info: Try loading font information for OMX+txex on input line 58. + + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/omxtxex.fd +File: omxtxex.fd 2000/12/15 v3.1 +) +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for U/txexa/m/n on input line 58. +LaTeX Font Info: Try loading font information for U+txexa on input line 58. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/utxexa.fd +File: utxexa.fd 2000/12/15 v3.1 +) +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 58. +LaTeX Font Info: ... okay on input line 58. +LaTeX Font Info: Try loading font information for OT1+txr on input line 58. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/ot1txr.fd +File: ot1txr.fd 2000/12/15 v3.1 +) +(/usr/share/texlive/texmf-dist/tex/context/base/supp-pdf.mkii +[Loading MPS to PDF converter (version 2006.09.02).] +\scratchcounter=\count112 +\scratchdimen=\dimen123 +\scratchbox=\box28 +\nofMPsegments=\count113 +\nofMParguments=\count114 +\everyMPshowfont=\toks26 +\MPscratchCnt=\count115 +\MPscratchDim=\dimen124 +\MPnumerator=\count116 +\makeMPintoPDFobject=\count117 +\everyMPtoPDFconversion=\toks27 +) (/usr/share/texlive/texmf-dist/tex/latex/oberdiek/epstopdf-base.sty +Package: epstopdf-base 2010/02/09 v2.5 Base part for package epstopdf + +(/usr/share/texlive/texmf-dist/tex/latex/oberdiek/grfext.sty +Package: grfext 2010/08/19 v1.1 Manage graphics extensions (HO) +) +Package grfext Info: Graphics extension search list: +(grfext) [.png,.pdf,.jpg,.mps,.jpeg,.jbig2,.jb2,.PNG,.PDF,.JPG,.JPE +G,.JBIG2,.JB2,.eps] +(grfext) \AppendGraphicsExtensions on input line 452. + +(/usr/share/texlive/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv +e +)) +\AtBeginShipoutBox=\box29 +Package hyperref Info: Link coloring OFF on input line 58. + +(/usr/share/texlive/texmf-dist/tex/latex/hyperref/nameref.sty +Package: nameref 2010/04/30 v2.40 Cross-referencing by name of section + +(/usr/share/texlive/texmf-dist/tex/generic/oberdiek/gettitlestring.sty +Package: gettitlestring 2010/12/03 v1.4 Cleanup title references (HO) +) +\c@section@level=\count118 +) +LaTeX Info: Redefining \ref on input line 58. +LaTeX Info: Redefining \pageref on input line 58. +LaTeX Info: Redefining \nameref on input line 58. + +(./main.out) (./main.out) +\@outlinefile=\write3 +\openout3 = `main.out'. + +LaTeX Font Info: Try loading font information for U+txsya on input line 64. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/utxsya.fd +File: utxsya.fd 2000/12/15 v3.1 +) +LaTeX Font Info: Try loading font information for U+txsyb on input line 64. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/utxsyb.fd +File: utxsyb.fd 2000/12/15 v3.1 +) +LaTeX Font Info: Try loading font information for U+txmia on input line 64. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/utxmia.fd +File: utxmia.fd 2000/12/15 v3.1 +) +LaTeX Font Info: Try loading font information for U+txsyc on input line 64. + +(/usr/share/texlive/texmf-dist/tex/latex/txfonts/utxsyc.fd +File: utxsyc.fd 2000/12/15 v3.1 +) +<./images/CC_88x31.png, id=92, 88.33pt x 31.11626pt> +File: ./images/CC_88x31.png Graphic file (type png) + + +Package pdftex.def Info: ./images/CC_88x31.png used on input line 64. +(pdftex.def) Requested size: 105.27359pt x 37.08528pt. + [1 + + +{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map} <./images/CC_88x31.png>] [2 + +] (./main.toc) +\tf@toc=\write4 +\openout4 = `main.toc'. + + + +Package Fancyhdr Warning: \headheight is too small (12.0pt): + Make it at least 14.49998pt. + We now make it that large for the rest of the document. + This may cause the page layout to be inconsistent, however. + +[3] (./abstract.tex [4 + +] +Chapter 1. +[5] [6 + +] +Chapter 2. +) (./laplace2D.tex [7] [8 + +] +Chapter 3. +[9] [10] <./images/Omega.png, id=189, 806.01125pt x 479.7925pt> +File: ./images/Omega.png Graphic file (type png) + + +Package pdftex.def Info: ./images/Omega.png used on input line 179. +(pdftex.def) Requested size: 210.54718pt x 125.32878pt. +) (./laplace3D.tex [11 <./images/Omega.png>] [12 + +] +Chapter 4. +[13] <./images/Omega2.png, id=218, 1132.23pt x 449.68pt> +File: ./images/Omega2.png Graphic file (type png) + + +Package pdftex.def Info: ./images/Omega2.png used on input line 117. +(pdftex.def) Requested size: 315.82881pt x 125.43608pt. + [14 <./images/Omega2.png>] [15] [16] [17] +<./images/Integral.png, id=281, 699.61375pt x 699.61375pt> +File: ./images/Integral.png Graphic file (type png) + + +Package pdftex.def Info: ./images/Integral.png used on input line 561. +(pdftex.def) Requested size: 315.82881pt x 315.82675pt. + +<./images/test_wave.png, id=283, 586.8324pt x 442.2924pt> +File: ./images/test_wave.png Graphic file (type png) + + +Package pdftex.def Info: ./images/test_wave.png used on input line 585. +(pdftex.def) Requested size: 315.82881pt x 238.04472pt. + [18] [19 <./images/Integral.png> <./images/test_wave.png>] <./images/test_dire +ct.png, id=301, 586.8324pt x 442.2924pt> +File: ./images/test_direct.png Graphic file (type png) + + +Package pdftex.def Info: ./images/test_direct.png used on input line 616. +(pdftex.def) Requested size: 315.82881pt x 238.04472pt. + +<./images/test_bem.png, id=304, 586.8324pt x 442.2924pt> +File: ./images/test_bem.png Graphic file (type png) + + +Package pdftex.def Info: ./images/test_bem.png used on input line 638. +(pdftex.def) Requested size: 315.82881pt x 238.04472pt. +) (./main.bbl [20 <./images/test_direct.png>] +[21 <./images/test_bem.png>] [22 + +]) +Package atveryend Info: Empty hook `BeforeClearDocument' on input line 90. + [23] +Package atveryend Info: Empty hook `AfterLastShipout' on input line 90. + (./main.aux) +Package atveryend Info: Executing hook `AtVeryEndDocument' on input line 90. +Package atveryend Info: Executing hook `AtEndAfterFileList' on input line 90. +Package rerunfilecheck Info: File `main.out' has not changed. +(rerunfilecheck) Checksum: 47992CAAC05675F8055DF1DA37B7F7A8;1434. +Package atveryend Info: Empty hook `AtVeryVeryEnd' on input line 90. + ) +Here is how much of TeX's memory you used: + 7457 strings out of 495059 + 103785 string characters out of 3182029 + 206801 words of memory out of 3000000 + 10379 multiletter control sequences out of 15000+200000 + 53484 words of font info for 126 fonts, out of 3000000 for 9000 + 14 hyphenation exceptions out of 8191 + 34i,17n,43p,237b,351s stack positions out of 5000i,500n,10000p,200000b,50000s +{/usr/share/texlive/texmf-dist/fonts/enc/dvips/base/8r.enc} +Output written on main.pdf (23 pages, 322361 bytes). +PDF statistics: + 376 PDF objects out of 1000 (max. 8388607) + 325 compressed objects within 4 object streams + 96 named destinations out of 1000 (max. 500000) + 212 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/src/Mod/Ship/simRun/theory/main.out b/src/Mod/Ship/simRun/theory/main.out new file mode 100644 index 000000000..dd5e06dbb --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.out @@ -0,0 +1,22 @@ +\BOOKMARK [0][-]{chapter.1}{Introduction}{}% 1 +\BOOKMARK [1][-]{section.1.1}{Objective}{chapter.1}% 2 +\BOOKMARK [0][-]{chapter.2}{Governing equations}{}% 3 +\BOOKMARK [0][-]{chapter.3}{Waves propagations in 2D plane}{}% 4 +\BOOKMARK [1][-]{section.3.1}{General}{chapter.3}% 5 +\BOOKMARK [1][-]{section.3.2}{Incident waves}{chapter.3}% 6 +\BOOKMARK [1][-]{section.3.3}{BEM applied to Laplace 2D problem}{chapter.3}% 7 +\BOOKMARK [1][-]{section.3.4}{Conclusions to Laplace 2D problem}{chapter.3}% 8 +\BOOKMARK [0][-]{chapter.4}{Waves propagations in 3D}{}% 9 +\BOOKMARK [1][-]{section.4.1}{General}{chapter.4}% 10 +\BOOKMARK [1][-]{section.4.2}{Incident waves}{chapter.4}% 11 +\BOOKMARK [1][-]{section.4.3}{BEM applied to Laplace 3D problem}{chapter.4}% 12 +\BOOKMARK [2][-]{subsection.4.3.1}{Computational domain}{section.4.3}% 13 +\BOOKMARK [2][-]{subsection.4.3.2}{Boundary conditions \(BC\)}{section.4.3}% 14 +\BOOKMARK [2][-]{subsection.4.3.3}{Time integration scheme}{section.4.3}% 15 +\BOOKMARK [2][-]{subsection.4.3.4}{Discrete Laplace solution using the BEM}{section.4.3}% 16 +\BOOKMARK [2][-]{subsection.4.3.5}{Integrals computation}{section.4.3}% 17 +\BOOKMARK [1][-]{section.4.4}{BEM test}{chapter.4}% 18 +\BOOKMARK [2][-]{subsection.4.4.1}{General}{section.4.4}% 19 +\BOOKMARK [2][-]{subsection.4.4.2}{Direct method}{section.4.4}% 20 +\BOOKMARK [2][-]{subsection.4.4.3}{BEM}{section.4.4}% 21 +\BOOKMARK [0][-]{chapter*.2}{Bibliography}{}% 22 diff --git a/src/Mod/Ship/simRun/theory/main.pdf b/src/Mod/Ship/simRun/theory/main.pdf new file mode 100644 index 000000000..798af56aa Binary files /dev/null and b/src/Mod/Ship/simRun/theory/main.pdf differ diff --git a/src/Mod/Ship/simRun/theory/main.tex b/src/Mod/Ship/simRun/theory/main.tex new file mode 100644 index 000000000..f09787f51 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.tex @@ -0,0 +1,90 @@ +\documentclass[a4paper,12pt]{book} +\usepackage[numbers]{natbib} +\bibliographystyle{plainnat} +\usepackage{graphicx} +\usepackage{verbatim} +\usepackage{color, colortbl} +\usepackage{hyperref} +\usepackage[utf8]{inputenc} +\setcounter{tocdepth}{2} +\usepackage{alltt} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{amsfonts} +\graphicspath{{./images/}} +\usepackage{txfonts} %%does not work on Windows +% \usepackage{dsfont} +% +\newcommand{\NAME}{FreeCAD-Ship } +\newcommand{\rc}{\\[0.2 in]} +\newcommand{\rcc}{\\[0.3 in]} +\newcommand{\rccc}{\\[0.4 in]} +% \newcommand{\bs}[1]{\boldsymbol{#1}} +\newcommand{\bs}[1]{\pmb{#1}} +\newcommand{\SPHint}{\int_{\bs{y} \in \Omega}} +\newcommand{\SPHboundint}{\int_{\bs{y} \in \partial \Omega}} +\newcommand{\gradient}{\nabla} +\newcommand{\divergence}{\mbox{div}} +\newcommand{\rotational}{\nabla \times} +\newcommand{\laplacian}{\bigtriangleup} +\newcommand{\dsty}{\displaystyle} +\newcommand{\sph}[1]{\langle {#1} \rangle} +% +\author{JL Cercos-Pita $<$jlcercos@gmail.com$>$} +\title{FreeCAD-Ship.\rcc +\textbf{Sea waves transport using Boundary Elements Method}} +\date{\today\\[5.0 in] +\begin{figure}[h!] + \centering + \includegraphics[width=0.2\textwidth]{CC_88x31} +\end{figure} +} +% +\setlength{\parindent}{0em} +% +\usepackage{anysize} +\marginsize{1.5cm}{1cm}{1cm}{1cm} +% +\usepackage{fancyhdr} +\pagestyle{fancyplain} +% Pages header and foot +\lhead{} +\rhead{\NAME} +\lfoot{JL Cercos-Pita} +\cfoot{jlcercos@gmail.com} +\rfoot{\thepage} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\begin{document} + +% ------------------------------- +% Title and summary +% ------------------------------- +\maketitle +\thispagestyle{empty} +% +\newpage +\tableofcontents +\newpage +\newpage +% ------------------------------- +% Introduction +% ------------------------------- +\input{abstract} + +% ------------------------------- +% Laplacian 2D +% ------------------------------- +\input{laplace2D} + +% ------------------------------- +% Laplacian 2D +% ------------------------------- +\input{laplace3D} + +% ------------------------------- +% Bibliography +% ------------------------------- +\bibliography{bib} +\addcontentsline{toc}{chapter}{Bibliography} +\end{document} diff --git a/src/Mod/Ship/simRun/theory/main.toc b/src/Mod/Ship/simRun/theory/main.toc new file mode 100644 index 000000000..c60c1e451 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/main.toc @@ -0,0 +1,22 @@ +\contentsline {chapter}{\numberline {1}Introduction}{5}{chapter.1} +\contentsline {section}{\numberline {1.1}Objective}{5}{section.1.1} +\contentsline {chapter}{\numberline {2}Governing equations}{7}{chapter.2} +\contentsline {chapter}{\numberline {3}Waves propagations in 2D plane}{9}{chapter.3} +\contentsline {section}{\numberline {3.1}General}{9}{section.3.1} +\contentsline {section}{\numberline {3.2}Incident waves}{9}{section.3.2} +\contentsline {section}{\numberline {3.3}BEM applied to Laplace 2D problem}{10}{section.3.3} +\contentsline {section}{\numberline {3.4}Conclusions to Laplace 2D problem}{11}{section.3.4} +\contentsline {chapter}{\numberline {4}Waves propagations in 3D}{13}{chapter.4} +\contentsline {section}{\numberline {4.1}General}{13}{section.4.1} +\contentsline {section}{\numberline {4.2}Incident waves}{13}{section.4.2} +\contentsline {section}{\numberline {4.3}BEM applied to Laplace 3D problem}{13}{section.4.3} +\contentsline {subsection}{\numberline {4.3.1}Computational domain}{13}{subsection.4.3.1} +\contentsline {subsection}{\numberline {4.3.2}Boundary conditions (BC)}{14}{subsection.4.3.2} +\contentsline {subsection}{\numberline {4.3.3}Time integration scheme}{15}{subsection.4.3.3} +\contentsline {subsection}{\numberline {4.3.4}Discrete Laplace solution using the BEM}{16}{subsection.4.3.4} +\contentsline {subsection}{\numberline {4.3.5}Integrals computation}{17}{subsection.4.3.5} +\contentsline {section}{\numberline {4.4}BEM test}{18}{section.4.4} +\contentsline {subsection}{\numberline {4.4.1}General}{18}{subsection.4.4.1} +\contentsline {subsection}{\numberline {4.4.2}Direct method}{19}{subsection.4.4.2} +\contentsline {subsection}{\numberline {4.4.3}BEM}{20}{subsection.4.4.3} +\contentsline {chapter}{Bibliography}{23}{chapter*.2} diff --git a/src/Mod/Ship/simRun/theory/test/green.py b/src/Mod/Ship/simRun/theory/test/green.py new file mode 100644 index 000000000..e8f22a530 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/test/green.py @@ -0,0 +1,127 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +from numpy import * + +import waves + +def G_val(x,X): + """ Computed the Green's function value. + @param x Point to evaluate (numpy.array(3)) + @param X Reference point (numpy.array(3)) + @return Green's function value. + """ + return 1.0 / (4.0*pi * linalg.norm(x-X)) + +def H_val(x,X): + """ Computed the Green's function gradient. + @param x Point to evaluate (numpy.array(3)) + @param X Reference point (numpy.array(3)) + @return Green's function gradient. + """ + return (x-X) / (4.0*pi * dot(x-X, x-X)**1.5 ) + +def GH(p,I,J,P,n,L,B,x,y,z,dx,dy,t, a,k,w,d): + """ Computes the Green's function integral + numerically, with an increase resolution of n. + @param p Point to evaluate + @param I Point to evaluate x index + @param J Point to evaluate y index + @param P Reference point + @param n Number of divisors in each direction to + discretize each area element. Should be an even value + @param L Length of the computational domain + @param B width of the computational domain + @param x X coordinates of the area elements + @param y Y coordinates of the area elements + @param z Z coordinates of the area elements + @param dx distance between elements in the x direction + @param dy distance between elements in the y direction + @param t Simulation time (s) + @param a List of waves amplitudes + @param k List of waves k + @param w List of waves omega + @param d List of waves lags. + @return Green's function integral. + """ + # Ensure that n is an even value. If n is even + # we can grant that any point will be placed + # in p, that can be eventually the same than + # P, being G and H functions bad defined. + if n % 2: + n = n + 1 + # Get the new distance between Green's points + DX = dx / n + DY = dy / n + # Get the coordinates of all the grid points + # around the evaluation one + nx = x.shape[0] + ny = y.shape[1] + X = zeros((3,3),dtype=float32) + Y = zeros((3,3),dtype=float32) + Z = zeros((3,3),dtype=float32) + for i in range(0,3): + for j in range(0,3): + X[i,j] = p[0] + (i-1)*dx + Y[i,j] = p[1] + (j-1)*dy + if((X[i,j] > -0.5*L) and (X[i,j] < 0.5*L) and (Y[i,j] > -0.5*B) and (Y[i,j] < 0.5*B)): + Z[i,j] = z[I-1+i,J-1+j] + else: + Z[i,j] = waves.z(X[i,j],Y[i,j],t, a,k,w,d) + # Perform spline surface coeffs + K = zeros((3*3),dtype=float32) + K[0] = Z[0,0] # k_{0} + K[1] = 4*Z[1,0] - Z[2,0] - 3*Z[0,0] # k_{u} + K[2] = 4*Z[0,1] - Z[0,2] - 3*Z[0,0] # k_{v} + K[3] = Z[2,2] - 4*Z[2,1] + 3*Z[2,0] + \ + 3*Z[0,2] - 12*Z[0,1] + 9*Z[0,0] + \ + -4*Z[1,2] + 16*Z[1,1] - 12*Z[1,0] # k_{uv} + K[4] = 2*Z[2,0] + 2*Z[0,0] - 4*Z[1,0] # k_{uu} + K[5] = 2*Z[0,2] + 2*Z[0,0] - 4*Z[0,1] # k_{vv} + K[6] = -2*Z[2,2] + 8*Z[2,1] - 6*Z[2,0] + \ + -2*Z[0,2] + 8*Z[0,1] - 6*Z[0,0] + \ + 4*Z[1,2] - 16*Z[1,1] + 12*Z[1,0] # k_{uuv} + K[7] = -2*Z[2,2] + 4*Z[2,1] - 2*Z[2,0] + \ + -6*Z[0,2] + 12*Z[0,1] - 6*Z[0,0] + \ + 8*Z[1,2] - 16*Z[1,1] + 8*Z[1,0] # k_{uuv} + K[8] = 4*Z[2,2] - 8*Z[2,1] + 4*Z[2,0] + \ + 4*Z[0,2] - 8*Z[0,1] + 4*Z[0,0] + \ + -8*Z[1,2] + 16*Z[1,1] - 8*Z[1,0] # k_{uuvv} + # Loop around the point p collecting the integral + G_tot = 0.0 + H_tot = zeros((3),dtype=float32) + for i in range(0,n): + for j in range(0,n): + xx = x[I,J] - 0.5*dx + (i+0.5)*DX + yy = y[I,J] - 0.5*dy + (j+0.5)*DY + # Interpolate z + u = (xx - X[0,0]) / (X[2,0] - X[0,0]) + v = (yy - Y[0,0]) / (Y[0,2] - Y[0,0]) + zz = K[0] + K[1]*u + K[2]*v + K[3]*u*v + \ + K[4]*u*u + K[5]*v*v + K[6]*u*u*v + \ + K[7]*u*v*v + K[8]*u*u*v*v + p = array([xx,yy,zz]) + G_tot = G_tot + G_val(p,P)*DX*DY + H_tot = H_tot + H_val(p,P)*DX*DY + return (G_tot,H_tot) + diff --git a/src/Mod/Ship/simRun/theory/test/main.py b/src/Mod/Ship/simRun/theory/test/main.py new file mode 100644 index 000000000..2b50f978a --- /dev/null +++ b/src/Mod/Ship/simRun/theory/test/main.py @@ -0,0 +1,285 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +import sys +from numpy import* +import scipy.linalg as la +from pylab import * + +from green import * +import waves + +# --------------------------------------------------- +# Input data +# --------------------------------------------------- + +# Computational free surface (the part where we will +# compute the fluid dynamics) definition +L = 20.0 +B = 10.0 +nx = 31 +ny = 15 + +# Waves to use, defined by the wave amplitude period, +# and lag. X direction waves will be assumed. +# l = g*T^2 / (2*pi) +a = [0.1] +t = [2.5] +d = [0.0] + +# Green's function maximum value (Error). Used to compute +# The maximum distance to extend the free surface. +G_error = 1e-1 + +# Green's functions integrals resolution (even values) +nG = 8 + +# --------------------------------------------------- +# Simulation setup +# --------------------------------------------------- + +# Discretization +drx = L / nx +dry = B / ny +dS = drx*dry + +# First we may compute how many domains must be +# included in each directions to reach the Green's +# function requested error. +x = (L-drx)/2.0 +Nx = 0 +Gx = G_error*10.0 +while(Gx > G_error): + x = x + L + Nx = Nx + 1 + p = zeros((3), dtype=float32) + P = zeros((3), dtype=float32) + p[0] = x + Gx = G_val(p,P) +print('{0} times the free surface must be duplicated in the x direction\n\tG={1}'.format(Nx,Gx)) + +y = (B-dry)/2.0 +Ny = 0 +Gy = G_error*10.0 +while(Gy > G_error): + y = y + B + Ny = Ny + 1 + p = zeros((3), dtype=float32) + P = zeros((3), dtype=float32) + p[1] = y + Gy = G_val(p,P) +print('{0} times the free surface must be duplicated in the y direction\n\tG={1}'.format(Ny,Gy)) + +# Now we can compute some additional data of the waves +k = [] +w = [] +c = [] +for i in range(0,len(a)): + w.append(2*pi/t[i]) + k.append(w[i]**2 / 9.81) + c.append(w[i]/k[i]) + +# We can intializate the free surface +x = zeros((nx,ny),dtype=float32) +y = zeros((nx,ny),dtype=float32) +z = zeros((nx,ny),dtype=float32) +gradz = zeros((nx,ny),dtype=float32) +p = zeros((nx,ny),dtype=float32) +gradp = zeros((nx,ny),dtype=float32) +for i in range(0,nx): + for j in range(0,ny): + x[i,j] = -0.5*L + (i+0.5)*drx + y[i,j] = -0.5*B + (j+0.5)*dry + z[i,j] = waves.z(x[i,j],y[i,j],0.0, a,k,w,d) + p[i,j] = waves.phi(x[i,j],y[i,j],z[i,j],0.0, a,k,w,d) + gradp[i,j] = waves.gradphi(x[i,j],y[i,j],z[i,j],0.0, a,k,w,d)[2] + +# We can plot starting data +plot_j = int(ny/2) +fig = figure() +plot(x[:,plot_j], z[:,plot_j], color="blue", linewidth=2.5, linestyle="-", label="z") +plot(x[:,plot_j], gradp[:,plot_j], color="red", linewidth=2.5, linestyle="-", label="vz") +plot(x[:,plot_j], p[:,plot_j], color="green", linewidth=2.5, linestyle="-", label="phi") +legend(loc='best') +grid() +show() +close(fig) + +# Compute the error in an arbitrary point +phi = zeros((nx,ny),dtype=float32) +i = int(nx/2) +j = int(ny/2) +xx = x[i,j] +yy = y[i,j] +zz = z[i,j] +pp = array([xx,yy,zz]) +print('Testing direct method in the point ({0}, {1}, {2})'.format(pp[0],pp[1],pp[2])) +for I in range(0,nx): + for J in range(0,ny): + # Get phi from this point + XX = x[I,J] + YY = y[I,J] + ZZ = z[I,J] + PP = array([XX,YY,ZZ]) + if((I,J) == (i,j)): + # if (I in range(i-1,i+2)) or (J in range(j-1,j+2)): + G,H = GH(PP,I,J,pp,nG,L,B,x,y,z,drx,dry,0.0, a,k,w,d) + else: + G = G_val(PP,pp)*dS + H = H_val(PP,pp)*dS + phi[i,j] = phi[i,j] - (p[I,J]*H[2] - gradp[I,J]*G) + # Get phi from the extended free surface + for II in range(-Nx,Nx+1): + for JJ in range(-Ny,Ny+1): + if (not II) and (not JJ): + # This is the computational domain + continue + XX = x[I,J] + II*L + YY = y[I,J] + JJ*B + ZZ = waves.z(XX,YY,0.0, a,k,w,d) + PP = array([XX,YY,ZZ]) + Phi = waves.phi(XX,YY,ZZ,0.0, a,k,w,d) + gPhi = waves.gradphi(XX,YY,ZZ,0.0, a,k,w,d)[2] + phi[i,j] = phi[i,j] - (Phi*H_val(PP,pp)[2] - gPhi*G_val(PP,pp))*dS +# Correct phi +phi[i,j] = 2.0*phi[i,j] +print('Computed = {0}, analitic = {1}\n'.format(phi[i,j],p[i,j])) + +# Compute the error along all the free surface +phi = zeros((nx,ny),dtype=float32) +error = 0.0 +done = 0 +print('Testing direct method in all the free surface') +for i in range(0,nx): + for j in range(0,ny): + if done != (100*(j + i*ny))/(nx*ny): + done = (100*(j + i*ny))/(nx*ny) + sys.stdout.write("{0}%...".format(done)) + sys.stdout.flush() + xx = x[i,j] + yy = y[i,j] + zz = z[i,j] + pp = array([xx,yy,zz]) + for I in range(0,nx): + for J in range(0,ny): + # Get phi from this point + XX = x[I,J] + YY = y[I,J] + ZZ = z[I,J] + PP = array([XX,YY,ZZ]) + if((I,J) == (i,j)): + # if (I in range(i-1,i+2)) or (J in range(j-1,j+2)): + G,H = GH(PP,I,J,pp,nG,L,B,x,y,z,drx,dry,0.0, a,k,w,d) + else: + G = G_val(PP,pp)*dS + H = H_val(PP,pp)*dS + phi[i,j] = phi[i,j] - (p[I,J]*H[2] - gradp[I,J]*G) + # Get phi from the extended free surface + for II in range(-Nx,Nx+1): + for JJ in range(-Ny,Ny+1): + if (not II) and (not JJ): + # This is the computational domain + continue + XX = x[I,J] + II*L + YY = y[I,J] + JJ*B + ZZ = waves.z(XX,YY,0.0, a,k,w,d) + PP = array([XX,YY,ZZ]) + Phi = waves.phi(XX,YY,ZZ,0.0, a,k,w,d) + gPhi = waves.gradphi(XX,YY,ZZ,0.0, a,k,w,d)[2] + phi[i,j] = phi[i,j] - (Phi*H_val(PP,pp)[2] - gPhi*G_val(PP,pp))*dS + # Correct phi + phi[i,j] = 2.0*phi[i,j] + error = error + (phi[i,j] - p[i,j])**2 +error = sqrt(error / (nx*ny)) +print('\nRoot mean square error = {0}\n'.format(error)) +fig = figure() +plot(x[:,plot_j], p[:,plot_j], color="blue", linewidth=2.5, linestyle="-", label="analitic") +plot(x[:,plot_j], phi[:,plot_j], color="red", linewidth=2.5, linestyle="-", label="interpolated") +legend(loc='best') +grid() +show() +close(fig) + +# Compute the BEM +GG = zeros((nx*ny, nx*ny), dtype=float32) +HH = -0.5 * array(identity(nx*ny),dtype=float32) +BB = zeros((nx*ny), dtype=float32) +error = 0.0 +done = 0 +print('Testing BEM in all the free surface') +for i in range(0,nx): + for j in range(0,ny): + if done != (100*(j + i*ny))/(nx*ny): + done = (100*(j + i*ny))/(nx*ny) + sys.stdout.write("{0}%...".format(done)) + sys.stdout.flush() + xx = x[i,j] + yy = y[i,j] + zz = z[i,j] + pp = array([xx,yy,zz]) + for I in range(0,nx): + for J in range(0,ny): + # Get phi from this point + XX = x[I,J] + YY = y[I,J] + ZZ = z[I,J] + PP = array([XX,YY,ZZ]) + if((I,J) == (i,j)): + # if (I in range(i-1,i+2)) or (J in range(j-1,j+2)): + G,H = GH(PP,I,J,pp,nG,L,B,x,y,z,drx,dry,0.0, a,k,w,d) + else: + G = G_val(PP,pp)*dS + H = H_val(PP,pp)*dS + GG[j + i*ny,J + I*ny] = GG[j + i*ny,J + I*ny] - G + HH[j + i*ny,J + I*ny] = HH[j + i*ny,J + I*ny] - H[2] + # Get phi from the extended free surface + for II in range(-Nx,Nx+1): + for JJ in range(-Ny,Ny+1): + if (not II) and (not JJ): + # This is the computational domain + continue + XX = x[I,J] + II*L + YY = y[I,J] + JJ*B + ZZ = waves.z(XX,YY,0.0, a,k,w,d) + PP = array([XX,YY,ZZ]) + Phi = waves.phi(XX,YY,ZZ,0.0, a,k,w,d) + gPhi = waves.gradphi(XX,YY,ZZ,0.0, a,k,w,d)[2] + BB[j + i*ny] = BB[j + i*ny] - (Phi*H_val(PP,pp)[2] - gPhi*G_val(PP,pp))*dS +[gPhi, residues, rank, s] = la.lstsq(GG, dot(HH,p.reshape(nx*ny)) + BB) +gPhi = gPhi.reshape(nx,ny) +if(rank < nx*ny): + print("Singular matrix G!.\n") + print("\tEffective rank of linear system matrix is %i (N = %i)\n" % (rank, nx*ny)) +for i in range(0,nx): + for j in range(0,ny): + error = error + (gPhi[i,j] - gradp[i,j])**2 +error = sqrt(error / (nx*ny)) +print('\nRoot mean square error = {0}\n'.format(error)) +fig = figure() +plot(x[:,plot_j], gradp[:,plot_j], color="blue", linewidth=2.5, linestyle="-", label="analitic") +plot(x[:,plot_j], gPhi[:,plot_j], color="red", linewidth=2.5, linestyle="-", label="interpolated") +legend(loc='best') +grid() +show() +close(fig) + diff --git a/src/Mod/Ship/simRun/theory/test/waves.py b/src/Mod/Ship/simRun/theory/test/waves.py new file mode 100644 index 000000000..1964be933 --- /dev/null +++ b/src/Mod/Ship/simRun/theory/test/waves.py @@ -0,0 +1,94 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program is distributed in the hope that it will be useful, * +#* but WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +#* GNU Library General Public License for more details. * +#* * +#* You should have received a copy of the GNU Library General Public * +#* License along with this program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +from numpy import * +from green import * + +def z(x,y,t, a,k,w,d): + """ Returns free surface shape + @param x X coordinate + @param y Y coordinate + @param t Time instant + @param a List of waves amplitudes + @param k List of waves k + @param w List of waves omega + @param d List of waves lags. + @return free surface elevation + """ + Z = 0.0 + for i in range(0,len(a)): + Z = Z + a[i]*sin(k[i]*x - w[i]*t + d[i]) + return Z + +def gradz(x,y,t, a,k,w,d): + """ Returns free surface shape gradient + @param x X coordinate + @param y Y coordinate + @param t Time instant + @param a List of waves amplitudes + @param k List of waves k + @param w List of waves omega + @param d List of waves lags. + @return free surface elevation + """ + gradZ = zeros((3), dtype=float32) + gradZ[2] = 1.0 + for i in range(0,len(a)): + gradZ[0] = gradZ[0] + a[i]*k[i]*cos(k[i]*x - w[i]*t + d[i]) + return gradZ + +def phi(x,y,z,t, a,k,w,d): + """ Returns velocity potential + @param x X coordinate + @param y Y coordinate + @param z Free surface elevation + @param t Time instant + @param a List of waves amplitudes + @param k List of waves k + @param w List of waves omega + @param d List of waves lags. + @return Velocity potential + """ + p = 0.0 + for i in range(0,len(a)): + p = p - a[i]*w[i]/k[i]*cos(k[i]*x - w[i]*t + d[i])*exp(k[i]*z) + return p + +def gradphi(x,y,z,t, a,k,w,d): + """ Returns velocity potential gradient + @param x X coordinate + @param y Y coordinate + @param z Free surface elevation + @param t Time instant + @param a List of waves amplitudes + @param k List of waves k + @param w List of waves omega + @param d List of waves lags. + @return velocity potential gradient + """ + gradp = zeros((3), dtype=float32) + for i in range(0,len(a)): + gradp[0] = gradp[0] + a[i]*w[i]*sin(k[i]*x - w[i]*t + d[i])*exp(k[i]*z) + gradp[2] = gradp[2] - a[i]*w[i]*cos(k[i]*x - w[i]*t + d[i])*exp(k[i]*z) + return gradp + diff --git a/src/Mod/Sketcher/App/PropertyConstraintList.cpp b/src/Mod/Sketcher/App/PropertyConstraintList.cpp index 6638c0a6c..b5825809e 100644 --- a/src/Mod/Sketcher/App/PropertyConstraintList.cpp +++ b/src/Mod/Sketcher/App/PropertyConstraintList.cpp @@ -126,7 +126,7 @@ void PropertyConstraintList::setPyObject(PyObject *value) if (!PyObject_TypeCheck(item, &(ConstraintPy::Type))) { std::string error = std::string("types in list must be 'Constraint', not "); error += item->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } values[i] = static_cast(item)->getConstraintPtr(); @@ -141,7 +141,7 @@ void PropertyConstraintList::setPyObject(PyObject *value) else { std::string error = std::string("type must be 'Constraint' or list of 'Constraint', not "); error += value->ob_type->tp_name; - throw Py::TypeError(error); + throw Base::TypeError(error); } } diff --git a/src/Mod/Sketcher/Gui/Command.cpp b/src/Mod/Sketcher/Gui/Command.cpp index 0dc6a3e2e..4ad5ae9d5 100644 --- a/src/Mod/Sketcher/Gui/Command.cpp +++ b/src/Mod/Sketcher/Gui/Command.cpp @@ -166,6 +166,88 @@ bool CmdSketcherNewSketch::isActive(void) return false; } +DEF_STD_CMD_A(CmdSketcherReorientSketch); + +CmdSketcherReorientSketch::CmdSketcherReorientSketch() + :Command("Sketcher_ReorientSketch") +{ + sAppModule = "Sketcher"; + sGroup = QT_TR_NOOP("Sketcher"); + sMenuText = QT_TR_NOOP("Reorient sketch..."); + sToolTipText = QT_TR_NOOP("Reorient the selected sketch"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; +} + +void CmdSketcherReorientSketch::activated(int iMsg) +{ + Sketcher::SketchObject* sketch = Gui::Selection().getObjectsOfType().front(); + if (sketch->Support.getValue()) { + int ret = QMessageBox::question(Gui::getMainWindow(), + qApp->translate("Sketcher_ReorientSketch","Sketch has support"), + qApp->translate("Sketcher_ReorientSketch","Sketch with a support face cannot be reoriented.\n" + "Do you want to detach it from the support?"), + QMessageBox::Yes|QMessageBox::No); + if (ret == QMessageBox::No) + return; + sketch->Support.setValue(0); + } + + // ask user for orientation + SketchOrientationDialog Dlg; + + if (Dlg.exec() != QDialog::Accepted) + return; // canceled + Base::Vector3d p = Dlg.Pos.getPosition(); + Base::Rotation r = Dlg.Pos.getRotation(); + + // do the right view direction + std::string camstring; + switch(Dlg.DirType){ + case 0: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n " + "position 0 0 87 \\n orientation 0 0 1 0 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n " + "aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }"; + break; + case 1: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n " + "position 0 0 -87 \\n orientation -1 0 0 3.1415927 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n " + "aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }"; + break; + case 2: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n " + "position 0 -87 0 \\n orientation -1 0 0 4.712389\\n nearDistance -112.88701\\n farDistance 287.28702\\n " + "aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; + break; + case 3: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n " + "position 0 87 0 \\n orientation 0 0.70710683 0.70710683 3.1415927\\n nearDistance -112.88701\\n farDistance 287.28702\\n " + "aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; + break; + case 4: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n " + "position 87 0 0 \\n orientation 0.57735026 0.57735026 0.57735026 2.0943952 \\n nearDistance -112.887\\n farDistance 287.28699\\n " + "aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; + break; + case 5: + camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n " + "position -87 0 0 \\n orientation -0.57735026 0.57735026 0.57735026 4.1887903 \\n nearDistance -112.887\\n farDistance 287.28699\\n " + "aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}"; + break; + } + + openCommand("Reorient Sketch"); + doCommand(Doc,"App.ActiveDocument.%s.Placement = App.Placement(App.Vector(%f,%f,%f),App.Rotation(%f,%f,%f,%f))" + ,sketch->getNameInDocument(),p.x,p.y,p.z,r[0],r[1],r[2],r[3]); + doCommand(Gui,"Gui.ActiveDocument.setEdit('%s')",sketch->getNameInDocument()); +} + +bool CmdSketcherReorientSketch::isActive(void) +{ + return Gui::Selection().countObjectsOfType + (Sketcher::SketchObject::getClassTypeId()) == 1; +} + DEF_STD_CMD_A(CmdSketcherMapSketch); CmdSketcherMapSketch::CmdSketcherMapSketch() @@ -345,6 +427,7 @@ void CreateSketcherCommands(void) Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); rcCmdMgr.addCommand(new CmdSketcherNewSketch()); + rcCmdMgr.addCommand(new CmdSketcherReorientSketch()); rcCmdMgr.addCommand(new CmdSketcherMapSketch()); rcCmdMgr.addCommand(new CmdSketcherLeaveSketch()); rcCmdMgr.addCommand(new CmdSketcherViewSketch()); diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index 80de72074..31a60bced 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -131,6 +131,7 @@ void EditDatumDialog::exec(bool atCursor) sketch->getNameInDocument(), ConstrNbr, newDatum); Gui::Command::commitCommand(); + Gui::Command::updateActive(); } catch (const Base::Exception& e) { QMessageBox::critical(qApp->activeWindow(), QObject::tr("Dimensional constraint"), QString::fromUtf8(e.what())); diff --git a/src/Mod/Sketcher/Gui/SoDatumLabel.cpp b/src/Mod/Sketcher/Gui/SoDatumLabel.cpp index 3db2fb096..3a07a6e88 100644 --- a/src/Mod/Sketcher/Gui/SoDatumLabel.cpp +++ b/src/Mod/Sketcher/Gui/SoDatumLabel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2011-2012 Luke Parry * + * Copyright (c) 2011-2012 Luke Parry * * * * This file is part of the FreeCAD CAx development system. * * * @@ -44,7 +44,6 @@ # include # include #endif - #include #include #include @@ -75,6 +74,7 @@ SoDatumLabel::SoDatumLabel() SO_NODE_ADD_FIELD(string, ("")); SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f,1.0f,1.0f))); SO_NODE_ADD_FIELD(pnts, (SbVec3f(.0f,.0f,.0f))); + SO_NODE_ADD_FIELD(norm, (SbVec3f(.0f,.0f,1.f))); SO_NODE_ADD_FIELD(name, ("Helvetica")); SO_NODE_ADD_FIELD(size, (12.f)); @@ -88,7 +88,7 @@ SoDatumLabel::SoDatumLabel() SO_NODE_DEFINE_ENUM_VALUE(Type, ANGLE); SO_NODE_DEFINE_ENUM_VALUE(Type, RADIUS); SO_NODE_SET_SF_ENUM_TYPE(datumtype, Type); - + SO_NODE_ADD_FIELD(param1, (0.f)); SO_NODE_ADD_FIELD(param2, (0.f)); @@ -123,7 +123,7 @@ void SoDatumLabel::drawImage() QImage image(w, h,QImage::Format_ARGB32_Premultiplied); image.fill(0x00000000); - + QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing); @@ -191,7 +191,7 @@ void SoDatumLabel::generatePrimitives(SoAction * action) float c = cos(angle); img1 = SbVec3f((img1[0] * c) - (img1[1] * s), (img1[0] * s) + (img1[1] * c), 0.f); - img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f); + img2 = SbVec3f((img2[0] * c) - (img2[1] * s), (img2[0] * s) + (img2[1] * c), 0.f); img3 = SbVec3f((img3[0] * c) - (img3[1] * s), (img3[0] * s) + (img3[1] * c), 0.f); img4 = SbVec3f((img4[0] * c) - (img4[1] * s), (img4[0] * s) + (img4[1] * c), 0.f); @@ -589,7 +589,7 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) // Get the Points SbVec3f p1 = pnts[0]; SbVec3f p2 = pnts[1]; - + SbVec3f dir = (p2-p1); dir.normalize(); SbVec3f norm (-dir[1],dir[0],0); @@ -668,7 +668,7 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) float range = this->param3.getValue(); float endangle = startangle + range; - + float r = 2*length; // Set the Text label angle to zero @@ -813,7 +813,7 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) std::vector corners; corners.push_back(p1); corners.push_back(p2); - + float minX = p1[0], minY = p1[1], maxX = p1[0] , maxY = p1[1]; for (std::vector::iterator it=corners.begin(); it != corners.end(); ++it) { minX = ((*it)[0] < minX) ? (*it)[0] : minX; @@ -829,18 +829,10 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) const unsigned char * dataptr = this->image.getValue(size, nc); - SbVec3f surfNorm(0.f, 0.f, 1.f) ; //Get the camera z-direction SbVec3f z = vv.zVector(); - const SbViewportRegion & vpr = SoViewportRegionElement::get(state); - SoGetMatrixAction getmatrixaction(vpr); - getmatrixaction.apply(action); - - SbMatrix transform = getmatrixaction.getMatrix(); - transform.multVecMatrix(surfNorm, surfNorm); - - bool flip = surfNorm.dot(z) > FLT_EPSILON; + bool flip = norm.getValue().dot(z) > FLT_EPSILON; glDisable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // Enable Textures @@ -850,7 +842,7 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) // deleting the texture. I guess we don't need this texture and thus // comment out the block. // #0000721: massive memory leak when dragging an unconstrained model - // + // #if 0 // Copy the text bitmap into memory and bind GLuint myTexture; diff --git a/src/Mod/Sketcher/Gui/SoDatumLabel.h b/src/Mod/Sketcher/Gui/SoDatumLabel.h index a57f8b37c..40ed7d9d0 100644 --- a/src/Mod/Sketcher/Gui/SoDatumLabel.h +++ b/src/Mod/Sketcher/Gui/SoDatumLabel.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ public: SoSFFloat param2; SoSFFloat param3; SoMFVec3f pnts; + SoSFVec3f norm; SoSFImage image; SoSFFloat lineWidth; diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 636100a5f..1d415ec10 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -1041,11 +1041,11 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2D &toPo } else { Base::Vector3d norm(-dir.y,dir.x,0); Constr->LabelDistance = vec.x * norm.x + vec.y * norm.y; - if (Constr->Type == Distance || - Constr->Type == DistanceX || Constr->Type == DistanceY) { + if (Constr->Type == Distance || + Constr->Type == DistanceX || Constr->Type == DistanceY) { vec = Base::Vector3d(toPos.fX, toPos.fY, 0) - (p2 + p1) / 2; Constr->LabelPosition = vec.x * dir.x + vec.y * dir.y; - } + } } } else if (Constr->Type == Angle) { @@ -1743,7 +1743,7 @@ void ViewProviderSketch::updateColor(void) type == Sketcher::Distance || type == Sketcher::DistanceX || type == Sketcher::DistanceY); - // Non DatumLabel Nodes will have a material excluding coincident + // Non DatumLabel Nodes will have a material excluding coincident bool hasMaterial = false; SoMaterial *m; @@ -1756,7 +1756,7 @@ void ViewProviderSketch::updateColor(void) if (hasDatumLabel) { SoDatumLabel *l = dynamic_cast(s->getChild(0)); l->textColor = SelectColor; - } else if (hasMaterial) + } else if (hasMaterial) m->diffuseColor = SelectColor; } else if (edit->PreselectConstraint == i) { if (hasDatumLabel) { @@ -2695,6 +2695,16 @@ void ViewProviderSketch::rebuildConstraintsVisual(void) SoMaterial *mat = new SoMaterial; mat->ref(); mat->diffuseColor = ConstrDimColor; + // Get sketch normal + Base::Vector3d RN(0,0,1); + + // move to position of Sketch + Base::Placement Plz = getSketchObject()->Placement.getValue(); + Base::Rotation tmp(Plz.getRotation()); + tmp.multVec(RN,RN); + Plz.setRotation(tmp); + + SbVec3f norm(RN.x, RN.y, RN.z); // distinguish different constraint types to build up switch ((*it)->Type) { @@ -2703,85 +2713,87 @@ void ViewProviderSketch::rebuildConstraintsVisual(void) case DistanceY: case Radius: case Angle: - { - SoDatumLabel *text = new SoDatumLabel(); - text->string = ""; - text->textColor = ConstrDimColor; - SoAnnotation *anno = new SoAnnotation(); - anno->renderCaching = SoSeparator::OFF; - anno->addChild(text); - sep->addChild(text); - edit->constrGroup->addChild(anno); - edit->vConstrType.push_back((*it)->Type); - // nodes not needed - sep->unref(); - mat->unref(); - continue; // jump to next constraint - } - break; + { + SoDatumLabel *text = new SoDatumLabel(); + text->norm.setValue(norm); + text->string = ""; + text->textColor = ConstrDimColor; + SoAnnotation *anno = new SoAnnotation(); + anno->renderCaching = SoSeparator::OFF; + anno->addChild(text); + sep->addChild(text); + edit->constrGroup->addChild(anno); + edit->vConstrType.push_back((*it)->Type); + // nodes not needed + sep->unref(); + mat->unref(); + continue; // jump to next constraint + } + break; case Horizontal: case Vertical: - { - sep->addChild(mat); - sep->addChild(new SoZoomTranslation()); // 1. - sep->addChild(new SoImage()); // 2. constraint icon + { + sep->addChild(mat); + sep->addChild(new SoZoomTranslation()); // 1. + sep->addChild(new SoImage()); // 2. constraint icon - // remember the type of this constraint node - edit->vConstrType.push_back((*it)->Type); - } - break; + // remember the type of this constraint node + edit->vConstrType.push_back((*it)->Type); + } + break; case Coincident: // no visual for coincident so far edit->vConstrType.push_back(Coincident); break; case Parallel: case Perpendicular: case Equal: - { - // Add new nodes to Constraint Seperator - sep->addChild(mat); - sep->addChild(new SoZoomTranslation()); // 1. - sep->addChild(new SoImage()); // 2. first constraint icon - sep->addChild(new SoZoomTranslation()); // 3. - sep->addChild(new SoImage()); // 4. second constraint icon + { + // Add new nodes to Constraint Seperator + sep->addChild(mat); + sep->addChild(new SoZoomTranslation()); // 1. + sep->addChild(new SoImage()); // 2. first constraint icon + sep->addChild(new SoZoomTranslation()); // 3. + sep->addChild(new SoImage()); // 4. second constraint icon - // remember the type of this constraint node - edit->vConstrType.push_back((*it)->Type); - } - break; + // remember the type of this constraint node + edit->vConstrType.push_back((*it)->Type); + } + break; case PointOnObject: case Tangent: - { - // Add new nodes to Constraint Seperator - sep->addChild(mat); - sep->addChild(new SoZoomTranslation()); // 1. - sep->addChild(new SoImage()); // 2. constraint icon + { + // Add new nodes to Constraint Seperator + sep->addChild(mat); + sep->addChild(new SoZoomTranslation()); // 1. + sep->addChild(new SoImage()); // 2. constraint icon - if ((*it)->Type == Tangent) { - const Part::Geometry *geo1 = getSketchObject()->getGeometry((*it)->First); - const Part::Geometry *geo2 = getSketchObject()->getGeometry((*it)->Second); - if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && - geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - sep->addChild(new SoZoomTranslation()); - sep->addChild(new SoImage()); // 3. second constraint icon + if ((*it)->Type == Tangent) { + const Part::Geometry *geo1 = getSketchObject()->getGeometry((*it)->First); + const Part::Geometry *geo2 = getSketchObject()->getGeometry((*it)->Second); + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + sep->addChild(new SoZoomTranslation()); + sep->addChild(new SoImage()); // 3. second constraint icon } - } - - edit->vConstrType.push_back((*it)->Type); } - break; + + edit->vConstrType.push_back((*it)->Type); + } + break; case Symmetric: - { - SoDatumLabel *arrows = new SoDatumLabel(); - arrows->string = ""; - arrows->textColor = ConstrDimColor; + { + SoDatumLabel *arrows = new SoDatumLabel(); + arrows->norm.setValue(norm); + arrows->string = ""; + arrows->textColor = ConstrDimColor; - sep->addChild(arrows); // 0. - sep->addChild(new SoTranslation()); // 1. - sep->addChild(new SoImage()); // 2. constraint icon + sep->addChild(arrows); // 0. + sep->addChild(new SoTranslation()); // 1. + sep->addChild(new SoImage()); // 2. constraint icon - edit->vConstrType.push_back((*it)->Type); - } - break; + edit->vConstrType.push_back((*it)->Type); + } + break; default: edit->vConstrType.push_back(None); } @@ -3195,7 +3207,7 @@ void ViewProviderSketch::setPositionText(const Base::Vector2D &Pos, const SbStri edit->textX->string = text; edit->textPos->translation = SbVec3f(Pos.fX,Pos.fY,zText); } - + void ViewProviderSketch::setPositionText(const Base::Vector2D &Pos) { SbString text; diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index ead382363..fda421b15 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -98,6 +98,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const << "Sketcher_LeaveSketch" << "Sketcher_ViewSketch" << "Sketcher_MapSketch" + << "Sketcher_ReorientSketch" << geom << cons ; diff --git a/src/Mod/TemplatePyMod/SplineSurface.py b/src/Mod/TemplatePyMod/SplineSurface.py new file mode 100644 index 000000000..b946b1293 --- /dev/null +++ b/src/Mod/TemplatePyMod/SplineSurface.py @@ -0,0 +1,45 @@ +# FreeCAD TemplatePyMod module +# (c) 2013 Werner Mayer LGPL + +# http://de.wikipedia.org/wiki/Non-Uniform_Rational_B-Spline +# len(knot_u) := nNodes_u + degree_u + 1 +# len(knot_v) := nNodes_v + degree_v + 1 + +degree_u=2 +degree_v=2 +nNodes_u=5 +nNodes_v=5 + +#knot_u=[0,0,0,0.3333,0.6666,1,1,1] +#knot_v=[0,0,0,0.3333,0.6666,1,1,1] +knot_u=[0,0,0,0.2,0.7,1,1,1] +knot_v=[0,0,0,0.2,0.7,1,1,1] +#knot_u=[0,0,0.2,0.4,0.6,0.8,1,1] +#knot_v=[0,0,0.2,0.4,0.6,0.8,1,1] +coor=[[0,0,1],[1,0,2],[2,0,0],[3,0,1],[4,0,2],\ + [0,1,2],[1,1,0],[2,1,0],[3,1,0],[4,1,0],\ + [0,2,0],[1,2,0],[2,2,0],[3,2,0],[4,2,0],\ + [0,3,1],[1,3,0],[2,3,0],[3,3,3],[4,3,0],\ + [0,4,2],[1,4,0],[2,4,0],[3,4,0],[4,4,0]] + +bs=Part.BSplineSurface() +bs.increaseDegree(degree_u,degree_v) + +id=1 +for i in range(0,len(knot_u)-1): + if knot_u[i+1] > knot_u[i]: + bs.insertUKnot(knot_u[i],id,0.0000001) + +id=1 +for i in range(0,len(knot_v)-1): + if knot_v[i+1] > knot_v[i]: + bs.insertVKnot(knot_v[i],id,0.0000001) + +i=0 +for jj in range(0,nNodes_v): + for ii in range(0,nNodes_u): + bs.setPole(ii+1,jj+1,FreeCAD.Vector((coor[i][0],coor[i][1],coor[i][2])),1); + i=i+1; + +s=bs.toShape() +Part.show(s) diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 981149fb4..2662adf42 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -180,7 +180,6 @@ class DocumentSaveRestoreCases(unittest.TestCase): def testSaveAndRestore(self): # saving and restoring SaveName = self.TempPath + os.sep + "SaveRestoreTests.FCStd" - self.Doc.FileName = SaveName self.failUnless(self.Doc.Label_1.TypeTransient == 4711) self.Doc.Label_1.TypeTransient = 4712 # setup Linking @@ -189,7 +188,7 @@ class DocumentSaveRestoreCases(unittest.TestCase): self.Doc.Label_1.LinkSub = (self.Doc.Label_2,["Sub1","Sub2"]) self.Doc.Label_2.LinkSub = (self.Doc.Label_1,["Sub3","Sub4"]) # save the document - self.Doc.save() + self.Doc.saveAs(SaveName) FreeCAD.closeDocument("SaveRestoreTests") self.Doc = FreeCAD.open(SaveName) self.failUnless(self.Doc.Label_1.Integer == 4711) @@ -207,8 +206,8 @@ class DocumentSaveRestoreCases(unittest.TestCase): Doc = FreeCAD.newDocument("RestoreTests") Doc.addObject("App::FeatureTest","Label_1") # saving and restoring - Doc.FileName = self.TempPath + os.sep + "Test2.FCStd" - Doc.save() + FileName = self.TempPath + os.sep + "Test2.FCStd" + Doc.saveAs(FileName) # restore must first clear the current content Doc.restore() self.failUnless(len(Doc.Objects) == 1) @@ -563,13 +562,12 @@ class DocumentPlatformCases(unittest.TestCase): self.Doc.addObject("App::FeatureTest", "Test") self.TempPath = tempfile.gettempdir() self.DocName = self.TempPath + os.sep + "PlatformTests.FCStd" - self.Doc.FileName = self.DocName def testFloatList(self): self.Doc.Test.FloatList = [-0.05, 2.5, 5.2] # saving and restoring - self.Doc.save() + self.Doc.saveAs(self.DocName) FreeCAD.closeDocument("PlatformTests") self.Doc = FreeCAD.open(self.DocName) @@ -581,7 +579,7 @@ class DocumentPlatformCases(unittest.TestCase): self.Doc.Test.ColourList = [(1.0,0.5,0.0),(0.0,0.5,1.0)] # saving and restoring - self.Doc.save() + self.Doc.saveAs(self.DocName) FreeCAD.closeDocument("PlatformTests") self.Doc = FreeCAD.open(self.DocName) @@ -598,7 +596,7 @@ class DocumentPlatformCases(unittest.TestCase): self.Doc.Test.VectorList = [(-0.05, 2.5, 5.2),(-0.05, 2.5, 5.2)] # saving and restoring - self.Doc.save() + self.Doc.saveAs(self.DocName) FreeCAD.closeDocument("PlatformTests") self.Doc = FreeCAD.open(self.DocName) @@ -609,7 +607,7 @@ class DocumentPlatformCases(unittest.TestCase): self.Doc.addObject("Points::Feature", "Points") # saving and restoring - self.Doc.save() + self.Doc.saveAs(self.DocName) FreeCAD.closeDocument("PlatformTests") self.Doc = FreeCAD.open(self.DocName) @@ -679,10 +677,10 @@ class DocumentFileIncludeCases(unittest.TestCase): self.failUnless(file.read()=="test No2") file.close() # Save restore test - self.Doc.FileName = self.TempPath+"/FileIncludeTest.fcstd" - self.Doc.save() + FileName = self.TempPath+"/FileIncludeTests.fcstd" + self.Doc.saveAs(FileName) FreeCAD.closeDocument("FileIncludeTests") - self.Doc = FreeCAD.open(self.TempPath+"/FileIncludeTest.fcstd") + self.Doc = FreeCAD.open(self.TempPath+"/FileIncludeTests.fcstd") # check if the file is still there self.L1 = self.Doc.getObject("FileObject1") file = open(self.L1.File,"r") @@ -715,10 +713,31 @@ class DocumentFileIncludeCases(unittest.TestCase): self.failUnless(file.read()=="test No2") file.close() + # create a second document, copy a file and close the document + # the test is about to put the file to the correct transient dir + doc2 = FreeCAD.newDocument("Doc2") + L4 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject") + L5 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject") + L6 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject") + L4.File = (L3.File,"Test.txt") + L5.File = L3.File + L6.File = L3.File + FreeCAD.closeDocument("FileIncludeTests") + self.Doc = FreeCAD.open(self.TempPath+"/FileIncludeTests.fcstd") + self.failUnless(os.path.exists(L4.File)) + self.failUnless(os.path.exists(L5.File)) + self.failUnless(os.path.exists(L6.File)) + self.failUnless(L5.File != L6.File) + # copy file from L5 which is in the same directory + L7 = doc2.addObject("App::DocumentObjectFileIncluded","FileObject3") + L7.File = (L5.File,"Copy.txt") + self.failUnless(os.path.exists(L5.File)) + FreeCAD.closeDocument("Doc2") + def tearDown(self): #closing doc - FreeCAD.closeDocument("FileIncludeTest") + FreeCAD.closeDocument("FileIncludeTests") class DocumentPropertyCases(unittest.TestCase): @@ -733,8 +752,7 @@ class DocumentPropertyCases(unittest.TestCase): self.Obj.addProperty(i,i) tempPath = tempfile.gettempdir() tempFile = tempPath + os.sep + "PropertyTests.FCStd" - self.Doc.FileName = tempFile - self.Doc.save() + self.Doc.saveAs(tempFile) FreeCAD.closeDocument("PropertyTests") self.Doc = FreeCAD.open(tempFile) diff --git a/src/Mod/Web/Gui/BrowserView.cpp b/src/Mod/Web/Gui/BrowserView.cpp index 5e53f872d..1a72d21c7 100644 --- a/src/Mod/Web/Gui/BrowserView.cpp +++ b/src/Mod/Web/Gui/BrowserView.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -119,6 +120,8 @@ BrowserView::BrowserView(QWidget* parent) this, SLOT(onLinkClicked(const QUrl &))); connect(view->page(), SIGNAL(downloadRequested(const QNetworkRequest &)), this, SLOT(onDownloadRequested(const QNetworkRequest &))); + connect(view->page(), SIGNAL(unsupportedContent(QNetworkReply*)), + this, SLOT(onUnsupportedContent(QNetworkReply*))); } /** Destroys the object and frees any allocated resources */ @@ -140,17 +143,7 @@ void BrowserView::onLinkClicked (const QUrl & url) //QString fragment = url. fragment(); if (scheme==QString::fromLatin1("http")) { - bool ok = false; - if (ok) { - //Dialog::DownloadDialog dlg (url,this/*QString::fromLatin1("c:/temp/test.fcstd")*/); - //int result = dlg.exec(); - //if(ext ==QString::fromLatin1("fcstd") ) - // Gui::Command::doCommand(Gui::Command::Gui,"Gui.open('c:/temp/test.fcstd')"); - } - else { - load(url); - } - //OpenURLInBrowser(url.toString().toLatin1()); + load(url); } // run scripts if not from somewhere else! if ((scheme.size() < 2 || scheme==QString::fromLatin1("file"))&& host.isEmpty()) { @@ -181,8 +174,18 @@ bool BrowserView::chckHostAllowed(const QString& host) void BrowserView::onDownloadRequested(const QNetworkRequest & request) { - Dialog::DownloadDialog dlg (request.url(),this); - dlg.exec(); + Gui::Dialog::DownloadManager::getInstance()->download(request); +} + +void BrowserView::onUnsupportedContent(QNetworkReply* reply) +{ + // Do not call handleUnsupportedContent() directly otherwise we won't get + // the metaDataChanged() signal of the reply. + Gui::Dialog::DownloadManager::getInstance()->download(reply->url()); + // Due to setting the policy QWebPage::DelegateAllLinks the onLinkClicked() + // slot is called even when clicking on a downloadable file but the page + // then fails to load. Thus, we reload the previous url. + view->reload(); } void BrowserView::load(const char* URL) diff --git a/src/Mod/Web/Gui/BrowserView.h b/src/Mod/Web/Gui/BrowserView.h index 553ccd160..64bcbd4b7 100644 --- a/src/Mod/Web/Gui/BrowserView.h +++ b/src/Mod/Web/Gui/BrowserView.h @@ -35,6 +35,7 @@ class QWebView; class QUrl; class QNetworkRequest; +class QNetworkReply; namespace WebGui { @@ -96,6 +97,7 @@ protected Q_SLOTS: void onLinkClicked (const QUrl& url); bool chckHostAllowed(const QString& host); void onDownloadRequested(const QNetworkRequest& request); + void onUnsupportedContent(QNetworkReply* reply); private: WebView* view; diff --git a/src/Tools/plugins/widget/plugin.cpp b/src/Tools/plugins/widget/plugin.cpp index 3769f37d4..e8f5a90f4 100644 --- a/src/Tools/plugins/widget/plugin.cpp +++ b/src/Tools/plugins/widget/plugin.cpp @@ -101,7 +101,13 @@ public: return false; } // QString codeTemplate() const; -// QString domXml() const; + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::UrlLabel"); @@ -143,6 +149,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::LocationWidget"); @@ -219,7 +232,13 @@ public: return false; } // QString codeTemplate() const; -// QString domXml() const; + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::FileChooser"); @@ -261,6 +280,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefFileChooser"); @@ -334,6 +360,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::AccelLineEdit"); @@ -407,6 +440,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::ActionSelector"); @@ -484,6 +524,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::CommandIconView"); @@ -557,6 +604,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::UIntSpinBox"); @@ -598,6 +652,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefSpinBox"); @@ -671,6 +732,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::ColorButton"); @@ -712,6 +780,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefColorButton"); @@ -784,6 +859,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefSlider"); @@ -855,6 +937,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefRadioButton"); @@ -926,6 +1015,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefCheckBox"); @@ -1001,6 +1097,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefComboBox"); @@ -1042,6 +1145,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefLineEdit"); @@ -1083,6 +1193,13 @@ public: { return false; } + QString domXml() const + { + return "\n" + " \n" + " \n" + ""; + } QString name() const { return QLatin1String("Gui::PrefDoubleSpinBox"); diff --git a/src/WindowsInstaller/BuildInstaller.bat b/src/WindowsInstaller/BuildInstaller.bat index 2ca4e2475..04fc554c7 100644 --- a/src/WindowsInstaller/BuildInstaller.bat +++ b/src/WindowsInstaller/BuildInstaller.bat @@ -24,7 +24,7 @@ SET /P M=Reebuild and press enter rem making of the bin zip file -c:\Programme\7-Zip\7z.exe a -t7z FreeCAD.7z "-xr!*.idb" "-xr!*.pdb" "-xr!*.ilk" "-xr!*.pyc" "-xr!?.git\*" "-xr!*.am" "-xr!CMakeFiles" "..\..\bin" "..\..\Mod" "..\..\Doc" "..\..\data" +"%PROGRAMFILES%\7-Zip\7z.exe" a -t7z FreeCAD.7z "-xr!*.idb" "-xr!*.pdb" "-xr!*.ilk" "-xr!*.pyc" "-xr!?.git\*" "-xr!*.am" "-xr!CMakeFiles" "..\..\bin" "..\..\Mod" "..\..\Doc" "..\..\data" call CopyRelease.bat diff --git a/src/WindowsInstaller/CopyRelease.bat.in b/src/WindowsInstaller/CopyRelease.bat.in index 6c7dbbb68..91d6e2330 100644 --- a/src/WindowsInstaller/CopyRelease.bat.in +++ b/src/WindowsInstaller/CopyRelease.bat.in @@ -1,4 +1,4 @@ -copy FreeCAD.msi FreeCAD_0.13.$WCREV$_x86_RC_setup.msi -copy FreeCAD.7z FreeCAD_0.13.$WCREV$_x86_RC_bin.7z \ No newline at end of file +copy FreeCAD.msi FreeCAD_0.14.$WCREV$_x86_RC_setup.msi +copy FreeCAD.7z FreeCAD_0.14.$WCREV$_x86_RC_bin.7z \ No newline at end of file diff --git a/src/WindowsInstaller/FreeCAD.wxs b/src/WindowsInstaller/FreeCAD.wxs index bd2d427f5..fd8e9e758 100644 --- a/src/WindowsInstaller/FreeCAD.wxs +++ b/src/WindowsInstaller/FreeCAD.wxs @@ -165,6 +165,7 @@ + diff --git a/src/WindowsInstaller/LibPack.wxs b/src/WindowsInstaller/LibPack.wxs index d49cc4ca6..007f69a33 100644 --- a/src/WindowsInstaller/LibPack.wxs +++ b/src/WindowsInstaller/LibPack.wxs @@ -59,16 +59,16 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/src/WindowsInstaller/ModArch.wxi b/src/WindowsInstaller/ModArch.wxi index d43d16aef..7e28c9302 100644 --- a/src/WindowsInstaller/ModArch.wxi +++ b/src/WindowsInstaller/ModArch.wxi @@ -29,7 +29,6 @@ - diff --git a/src/WindowsInstaller/ModDraft.wxi b/src/WindowsInstaller/ModDraft.wxi index eb43fee40..2a212d379 100644 --- a/src/WindowsInstaller/ModDraft.wxi +++ b/src/WindowsInstaller/ModDraft.wxi @@ -42,14 +42,5 @@ - - - - - - - - - diff --git a/src/WindowsInstaller/ModShip.wxi b/src/WindowsInstaller/ModShip.wxi index 03386f5e3..9e30d9210 100644 --- a/src/WindowsInstaller/ModShip.wxi +++ b/src/WindowsInstaller/ModShip.wxi @@ -24,12 +24,12 @@ - - - + + + @@ -45,6 +45,14 @@ + + + + + + + + @@ -118,17 +126,26 @@ - - - + + + + + + + + + + + + - +