--- /dev/null
+if (NOT USE_SYSTEM_CGAL)
+ # Enable ExternalProject CMake module
+ include(ExternalProject)
+
+ # Set default ExternalProject root directory
+ set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/3rdparty)
+
+ message(STATUS "Building CGAL")
+
+ message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}")
+ message(STATUS "Binary dir: ${CMAKE_BINARY_DIR}")
+
+ set(CGAL_INSTALL_DIR ${CMAKE_BINARY_DIR}/CGAL-installdir)
+
+ if ( CMAKE_BUILD_TYPE )
+ set(CGAL_BUILD_TYPE ${CMAKE_BUILD_TYPE})
+ else()
+ set(CGAL_BUILD_TYPE Debug)
+ endif()
+
+ # CGAL doesn't allow Developer, RelWithDebInfo and MinSizeRel as build type.
+ if ( "${CMAKE_BUILD_TYPE}" STREQUAL "Developer" )
+ set(CGAL_BUILD_TYPE "Debug")
+ endif()
+ if ( "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel"
+ OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" )
+ set(CGAL_BUILD_TYPE "Release")
+ endif()
+
+ message(STATUS "CGAL build type: ${CGAL_BUILD_TYPE}")
+
+ # Note: Need to add fPIC in order to be able to link staticly with CGAL
+ # FIXME: The CMAKE_CXX_FLAGS=-std=c++11 is a hack to remove CGAL's dependency on boost.thread (see setup_Boost.cmake)
+ # This should be removed when CGAL's boost dependency is eventually removed.
+ ExternalProject_Add(CGAL
+ SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/CGAL
+ INSTALL_DIR ${CGAL_INSTALL_DIR}
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${CGAL_INSTALL_DIR} -DCMAKE_BUILD_TYPE=${CGAL_BUILD_TYPE} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_CXX_FLAGS=-std=c++11 -DCGAL_CXX_FLAGS=-fPIC -DWITH_CGAL_Core:BOOL=OFF -DWITH_CGAL_ImageIO:BOOL=OFF -DWITH_CGAL_Qt5:BOOL=OFF -DWITH_demos:BOOL=OFF -DWITH_examples:BOOL=OFF -DWITH_LEDA:BOOL=OFF -DWITH_GMP:BOOL=ON -DWITH_MPFR:BOOL=ON -DWITH_Eigen3:BOOL=ON -DBUILD_SHARED_LIBS:BOOL=OFF -DBOOST_ROOT=${BOOST_ROOT} -DGMP_INCLUDE_DIR=${GMP_INCLUDE_DIR} -DGMP_LIBRARIES=${GMP_LIBRARIES} -DMPFR_INCLUDE_DIR=${MPFR_INCLUDE_DIR} -DMPFR_LIBRARIES=${MPFR_LIBRARIES} -DEIGEN3_INCLUDE_DIR=${EIGEN3_INCLUDE_DIR} -DCGAL_HEADER_ONLY:BOOL=${CGAL_HEADER_ONLY}
+
+ # -DWITH_ESBTL:BOOL=OFF \
+ # -DWITH_GMPXX:BOOL=OFF \
+ # -DWITH_IPE:BOOL=OFF \
+ # -DWITH_LAPACK:BOOL=OFF \
+ # -DWITH_LEDA:BOOL=OFF \
+ # -DWITH_MPFI:BOOL=OFF \
+ # -DWITH_NTL:BOOL=OFF \
+ # -DWITH_OpenGL:BOOL=OFF \
+ # -DWITH_OpenNL:BOOL=OFF \
+ # -DWITH_QGLViewer:BOOL=OFF \
+ # -DWITH_RS:BOOL=OFF \
+ # -DWITH_RS3:BOOL=OFF \
+ # -DWITH_TAUCS:BOOL=OFF \
+ # -DWITH_ZLIB:BOOL=OFF \
+
+ UPDATE_COMMAND ""
+ )
+
+ set(CGAL_INCLUDE_DIR ${CGAL_INSTALL_DIR}/include PARENT_SCOPE)
+ set(CGAL_LIB_DIR ${CGAL_INSTALL_DIR}/lib PARENT_SCOPE)
+
+ # LINK_DIRECTORIES( ${CGAL_INSTALL_DIR}/lib )
+
+ # ADD_LIBRARY(CGAL STATIC IMPORTED)
+ # SET_TARGET_PROPERTIES(TARGET cgal PROPERTY
+ # IMPORTED_LOCATION ${CGAL_INSTALL_DIR}/lib/libCGAL.a)
+
+ # ADD_LIBRARY(CGAL_Core STATIC IMPORTED)
+ # SET_TARGET_PROPERTIES(TARGET cgal PROPERTY
+ # IMPORTED_LOCATION ${CGAL_INSTALL_DIR}/lib/libCGAL_Core.a)
+endif()
+
+if (NOT USE_SYSTEM_TETGEN)
+ add_subdirectory( tetgen1.5.0 )
+endif()
+
+set(EXTERNAL_INCLUDE_DIRS "${CGAL_INSTALL_DIR}/include;${TETGEN_INCLUDE_DIR}" PARENT_SCOPE)
+set(EXTERNAL_LIBS "tet" PARENT_SCOPE)
+set(EXTERNAL_DEFINITIONS "${TETGEN_DEFINITIONS}" PARENT_SCOPE)
--- /dev/null
+diff --git a/3rdparty/CGAL/CMakeLists.txt b/3rdparty/CGAL/CMakeLists.txt
+index d5173fd..0ae9026 100644
+--- a/3rdparty/CGAL/CMakeLists.txt
++++ b/3rdparty/CGAL/CMakeLists.txt
+@@ -890,7 +890,9 @@ foreach (dir ${CGAL_CONFIGURED_PACKAGES})
+ install(DIRECTORY ${dir}/include/CGAL DESTINATION ${CGAL_INSTALL_INC_DIR} PATTERN ".svn" EXCLUDE)
+ endif()
+ endforeach()
+-install(DIRECTORY ${CMAKE_BINARY_DIR}/include/CGAL DESTINATION ${CGAL_INSTALL_INC_DIR} PATTERN ".svn" EXCLUDE)
++if(EXISTS ${CMAKE_BINARY_DIR}/include/CGAL)
++ install(DIRECTORY ${CMAKE_BINARY_DIR}/include/CGAL DESTINATION ${CGAL_INSTALL_INC_DIR} PATTERN ".svn" EXCLUDE)
++endif()
+
+ file(GLOB scripts "scripts/*")
+ list(SORT scripts)
--- /dev/null
+# Require CMake 3.5
+cmake_minimum_required(VERSION 3.5)
+
+project( MSHR )
+set(MSHR_VERSION_RELEASE 1)
+set(MSHR_VERSION_MAJOR "2018")
+set(MSHR_VERSION_MINOR "1")
+set(MSHR_VERSION_MICRO "0")
+set(MSHR_VERSION "${MSHR_VERSION_MAJOR}.${MSHR_VERSION_MINOR}.${MSHR_VERSION_MICRO}")
+if (NOT MSHR_VERSION_RELEASE)
+ set(MSHR_VERSION "${MSHR_VERSION}.dev0")
+endif()
+
+# CGAL setup
+option(USE_SYSTEM_CGAL "Do not build CGAL, but use an existing build instead." OFF)
+if (USE_SYSTEM_CGAL)
+ find_package(CGAL 4.12 CONFIG REQUIRED)
+endif()
+
+# Borrow some cmake modules from cgal
+if (USE_SYSTEM_CGAL)
+ set(CMAKE_MODULE_PATH "${CGAL_MODULES_DIR}")
+else()
+ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/CGAL/cmake/modules")
+endif()
+
+# Add cmake directory to module path
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+
+option(USE_SYSTEM_TETGEN "Do not build tetgen, but use an existing build instead." OFF)
+if (USE_SYSTEM_TETGEN)
+ find_package(TetGen)
+endif()
+
+# Helper macro for testing if particular value is contained in list
+# Taken from http://www.cmake.org/Wiki/CMakeMacroListOperations#LIST_CONTAINS
+MACRO(LIST_CONTAINS var value)
+ SET(${var})
+ FOREACH (value2 ${ARGN})
+ IF (${value} STREQUAL ${value2})
+ SET(${var} TRUE)
+ ENDIF (${value} STREQUAL ${value2})
+ ENDFOREACH (value2)
+ENDMACRO(LIST_CONTAINS)
+
+# Use C++11
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")
+
+# Boost
+# This is workaround to avoid that find_package(Boost)
+# picks up th ewrong boost when a hint is given
+set(BOOST_ROOT $ENV{BOOST_DIR} $ENV{BOOST_HOME})
+if (BOOST_ROOT)
+ set(Boost_NO_SYSTEM_PATHS on)
+endif()
+
+# Prevent FindBoost.cmake from looking for system Boost{foo}.cmake files
+set(Boost_NO_BOOST_CMAKE true)
+find_package( Boost REQUIRED system filesystem program_options )
+# Save this because it will be overwritten by find_package(DOLFIN)
+set(Boost_MSHR_LIBRARIES ${Boost_LIBRARIES})
+include_directories("${Boost_INCLUDE_DIRS}")
+
+# GMP_INCLUDE_DIR - the GMP include directory
+# GMP_LIBRARIES_DIR - directory where the GMP libraries are located
+# GMP_LIBRARIES - Link these to use GMP
+find_package(GMP REQUIRED)
+include_directories("${GMP_INCLUDE_DIR}")
+
+# Try to find the MPFR libraries
+# MPFR_FOUND - system has MPFR lib
+# MPFR_INCLUDE_DIR - the MPFR include directory
+# MPFR_LIBRARIES_DIR - Directory where the MPFR libraries are located
+# MPFR_LIBRARIES - the MPFR libraries
+# MPFR_IN_CGAL_AUXILIARY - TRUE if the MPFR found is the one distributed with CGAL in the auxiliary folder
+find_package(MPFR REQUIRED)
+include_directories("${MPFR_INCLUDE_DIR}")
+
+find_package(Eigen3 REQUIRED)
+include_directories("${EIGEN3_INCLUDE_DIR}")
+
+# use CGAL in header-only mode
+option (CGAL_HEADER_ONLY "Use CGAL in header-only mode" ON)
+if (CGAL_HEADER_ONLY)
+ add_definitions( -DCGAL_HEADER_ONLY )
+endif()
+
+add_subdirectory(3rdparty)
+#include_directories(BEFORE ${CGAL_INCLUDE_DIR})
+include_directories(BEFORE ${EXTERNAL_INCLUDE_DIRS})
+add_definitions("${EXTERNAL_DEFINITIONS}")
+
+find_package(DOLFIN REQUIRED)
+include_directories(${DOLFIN_PYTHON_INCLUDE_DIRS})
+include(${DOLFIN_USE_FILE})
+
+# Set installation directories
+set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries")
+set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables")
+set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files")
+set(SHARE_DIR "share/mshr" CACHE PATH "Shared data installation directory.")
+
+# Make relative paths absolute (needed later on)
+foreach(p LIB BIN INCLUDE CMAKE)
+ set(var INSTALL_${p}_DIR)
+ if(NOT IS_ABSOLUTE "${${var}}")
+ set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}")
+ endif()
+endforeach()
+
+
+# include for local header directory
+include_directories( BEFORE include )
+
+file( GLOB_RECURSE SOURCES src/*.cpp )
+
+add_library(mshr SHARED ${SOURCES})
+if (NOT USE_SYSTEM_CGAL)
+ add_dependencies( mshr CGAL )
+endif()
+
+# Append the library version information to the library target properties
+option(MSHR_WITH_LIBRARY_VERSION "Build with library version information." ON)
+if (MSHR_WITH_LIBRARY_VERSION)
+ string(REPLACE "+" "" MSHR_LIBRARY_VERSION ${MSHR_VERSION})
+ # This setting of SOVERSION assumes that any API change
+ # will increment either the minor or major version number.
+ set(MSHR_LIBRARY_PROPERTIES ${MSHR_LIBRARY_PROPERTIES}
+ VERSION ${MSHR_LIBRARY_VERSION}
+ SOVERSION ${MSHR_VERSION_MAJOR}.${MSHR_VERSION_MINOR}
+ )
+endif()
+set_target_properties(mshr PROPERTIES ${MSHR_LIBRARY_PROPERTIES})
+
+# Link the executable to (static) CGAL libraries
+target_link_libraries( mshr ${Boost_MSHR_LIBRARIES}
+ ${GMP_LIBRARIES}
+ ${MPFR_LIBRARIES}
+ tet
+ ${DOLFIN_LIBRARIES}
+ )
+
+# Fix CGAL's random generator (usefull to reproduce bugs)
+option(INIT_CGAL_RANDOM "Use fixed seed for CGAL's pseudo random generator" OFF)
+if (INIT_CGAL_RANDOM)
+ add_definitions( -DINIT_RANDOM_GENERATOR=0 )
+endif()
+
+#
+option(ENABLE_EXPERIMENTAL "Enable experimental code" OFF)
+if(ENABLE_EXPERIMENTAL)
+ message(STATUS "Enabling experimental code")
+ add_definitions( -DMSHR_ENABLE_EXPERIMENTAL )
+endif()
+
+
+export(PACKAGE mshr)
+
+target_link_libraries( mshr
+ ${DOLFIN_LIBRARIES}
+ ${Boost_MSHR_LIBRARIES}
+ )
+
+# install library
+install(TARGETS mshr
+ RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+ LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+ ARCHIVE DESTINATION ${INSTALL_LIB_DIR}
+ )
+
+option(ENABLE_MSHRABLE "Enable building the command line client, mshrable" OFF)
+if (ENABLE_MSHRABLE)
+ add_executable( mshrable app/mshrable.cpp )
+
+ target_link_libraries( mshrable
+ mshr
+ ${DOLFIN_LIBRARIES}
+ ${Boost_MSHR_LIBRARIES}
+ )
+
+ # install app
+ install(TARGETS mshrable
+ RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+ LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+ ARCHIVE DESTINATION ${INSTALL_LIB_DIR}
+ )
+else()
+ message(STATUS "Disabling building command line client mshrable")
+endif()
+
+# install header files
+install(DIRECTORY include/ DESTINATION ${INSTALL_INCLUDE_DIR})
+
+file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${INSTALL_INCLUDE_DIR}")
+
+### add cmake configuration file ###
+
+# common for both config files
+set(CONF_EXTERNAL_INCLUDE_DIRS "${DOLFIN_PYTHON_INCLUDE_DIRS};${DOLFIN_INCLUDE_DIRS};${DOLFIN_3RD_PARTY_INCLUDE_DIRS}")
+set(CONF_EXTERNAL_LIBRARIES "${DOLFIN_PYTHON_LIBRARIES};${DOLFIN_LIBRARIES};${DOLFIN_3RD_PARTY_LIBRARIES}")
+set(CONF_CXX_DEFINITIONS "${DOLFIN_CXX_DEFINITIONS}")
+string(REPLACE "\"" "\\\"" CONF_CXX_DEFINITIONS "${CONF_CXX_DEFINITIONS}")
+set(CONF_CXX_FLAGS "${DOLFIN_CXX_FLAGS}")
+
+# for the build tree
+set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/include")
+set(CONF_LIBRARIES_DIRS "${PROJECT_BINARY_DIR}")
+
+configure_file(mshrConfig.cmake.in "${PROJECT_BINARY_DIR}/mshrConfig.cmake" @ONLY)
+configure_file(mshr-config.cmake.in "${PROJECT_BINARY_DIR}/mshr-config.cmake" @ONLY)
+
+# ... and for the install tree
+set(CONF_INCLUDE_DIRS "${INSTALL_INCLUDE_DIR}")
+set(CONF_LIBRARIES_DIRS "${INSTALL_LIB_DIR}")
+configure_file(mshrConfig.cmake.in
+ "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/mshrConfig.cmake" @ONLY)
+configure_file(mshr-config.cmake.in
+ "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/mshr-config.cmake" @ONLY)
+
+# Install the FooBarConfig.cmake and FooBarConfigVersion.cmake
+install(FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/mshrConfig.cmake" DESTINATION "${SHARE_DIR}/cmake" COMPONENT dev)
+install(FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/mshr-config.cmake" DESTINATION "${SHARE_DIR}/cmake" COMPONENT dev)
+#install(FILES "Use-mshr.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev)
+
+
+
+
+
+option(ENABLE_TESTS "Enable testing" OFF)
+
+if(ENABLE_TESTS)
+ enable_testing()
+ add_subdirectory(test)
+else()
+ message(STATUS "Testing disabled")
+endif()
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+Changelog
+=========
+
+2018.1.0 (2018-06-14)
+--------------
+
+- Drop python2 support.
+- Switch to pybind11 bindings from SWIG.
+
+2017.2.0 (2017-12-05)
+---------------------
+
+- Nothing changed yet
+
+2017.1.0 (2017-05-11)
+---------------------
+
+- Bugfixes
+
+2016.2.0 (2016-11-30)
+---------------------
+
+- Improvements and bugfixes
+- Upgrade to CGAL 4.9 and use CGAL as header only library
+
+2016.1.0 (2016-06-23)
+---------------------
+
+- Major improvements, in particular boundary conditions
+
+1.6.0 (2015-07-28)
+------------------
+
+- Upgrade to CGAL 4.6
+- Add more demos
+- Faster and more memory efficient implementation of csg operations
+ with -DENABLE_EXPERIMENTAL=True
+- Bugfixes and cleanups
+
+1.5.0 (2015-02-04)
+------------------
+
+- Initial release of mshr.
--- /dev/null
+====\r
+mshr\r
+====\r
+\r
+mshr is the mesh generation component of `FEniCS\r
+<http://fenicsproject.org/>`_. It generates simplicial `DOLFIN\r
+<https://bitbucket.org/fenics-project/dolfin>`_ meshes in 2D and 3D\r
+from geometries described by Constructive Solid Geometry (CSG) or from\r
+surface files, utilizing CGAL and Tetgen as mesh generation backends.\r
+\r
+Authors:\r
+ | Benjamin Kehlet <benjamik@simula.no>\r
+\r
+Contributors:\r
+ | Anders Logg <logg@chalmers.se>\r
+ | Johannes Ring <johannr@simula.no>\r
+ | Garth N. Wells <gnw20@cam.ac.uk>\r
+\r
+Documentation\r
+=============\r
+\r
+The documentation is currently being prepared here: \r
+`https://bitbucket.org/fenics-project/mshr/wiki <https://bitbucket.org/fenics-project/mshr/wiki>`_\r
+\r
+Installation\r
+============\r
+For Debian and Ubuntu users, installing mshr is as easy as::\r
+\r
+ sudo apt-get install fenics\r
+\r
+To get a recent version of, enable the FEniCS PPA first. See `Installation instructions for Ubuntu <http://fenicsproject.org/download/ubuntu_details.html>`_\r
+\r
+To build mshr from source, run::\r
+\r
+ cmake <path to mshr source tree>\r
+ make\r
+ make install\r
+\r
+mshr's build script will also build CGAL and Tetgen from source and\r
+include them in the binary.\r
+\r
+Dependencies\r
+============\r
+\r
+mshr needs `DOLFIN <https://bitbucket.org/fenics-project/dolfin>`_\r
+with Python support (pyDolfin). `CGAL <http://www.cgal.org/>`_ and\r
+`Tetgen <http://www.tetgen.org>`_ are shipped with mshr and built from\r
+source automatically. CGAL needs `Gnu GMP <https://gmplib.org/>`_ and\r
+`Gnu MPFR <http://www.mpfr.org/>`_.\r
+\r
+License\r
+=======\r
+\r
+mshr is licensed under GPL version 3 or (at your option) any later\r
+version.\r
+\r
+Contact\r
+=======\r
+\r
+mshr is hosted at https://bitbucket.org/fenics-project/mshr/\r
+\r
+For comments and requests, send an email to the FEniCS mailing list::\r
+\r
+ fenics@fenicsproject.org\r
+\r
+For bug reports and feature requests, visit mshr's issue tracker at BitBucket::\r
+\r
+ https://bitbucket.org/fenics-project/mshr/issues\r
+\r
+Contributions\r
+=============\r
+\r
+Contributions are welcome!\r
+\r
+Please read about contributing to FEniCS here:\r
+http://fenicsproject.org/contributing/\r
+\r
+If you plan to implement a new feature, please discuss it at the\r
+FEniCS mailing list beforehand. Smaller patches and bugfixes are\r
+easiest submitted as `pull request on Bitbucket\r
+<https://confluence.atlassian.com/display/BITBUCKET/Work+with+pull+requests>`_.
\ No newline at end of file
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr.h>
+#include <dolfin.h>
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <boost/filesystem.hpp>
+
+#include <string>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+// This program reads in a surface from file and generates mesh
+
+
+namespace
+{
+void print_mesh_statistics(const dolfin::Mesh& m)
+{
+ std::cout << "Dolfin mesh of topological dimension " << m.topology().dim() << std::endl;
+ std::cout << " " << m.num_vertices() << " vertices" << std::endl;
+ std::cout << " " << m.num_cells() << " cells" << std::endl;
+
+ const std::pair<double, double> volume_min_max = mshr::DolfinMeshUtils::cell_volume_min_max(m);
+ std::cout << "Min cell volume: " << volume_min_max.first << std::endl;
+ std::cout << "Max cell volume: " << volume_min_max.second << std::endl;
+ const std::pair<double, double> radii_ratio = dolfin::MeshQuality::radius_ratio_min_max(m);
+ std::cout << "Minimum cell radii ratio: " << radii_ratio.first << std::endl;
+ std::cout << "Maximum cell radii ratio: " << radii_ratio.second << std::endl;
+}
+//-----------------------------------------------------------------------------
+// Define options and parse command line
+void handle_commandline(int argc, char** argv, po::variables_map &vm)
+{
+ // Command line options
+ po::options_description visible("Generate mesh from surface file");
+ visible.add_options()
+ ("outfile,o", po::value<std::string>(), "Filename of generated Dolfin mesh")
+ ("resolution,r", po::value<double>()->default_value(15.0), "Resolution of result mesh")
+ ("stats,s", "Write some statistics of the mesh to stdout")
+ ("polyout", po::value<std::string>(), "Write the polyhedron to a .off file (and do not create a mesh)")
+ ("polystats", "Write statistics of polyhedron (and do not create a mesh")
+ ("backend,b", po::value<std::string>()->default_value("cgal"), "Use 3D mesh generation backend [tetgen|cgal]")
+ ("degenerate_tolerance", po::value<double>()->default_value(1e-12), "Tolerance for considering a facet as degenerate. Set to 0 to not remove degenerate facets")
+ ("check-mesh", "Check consistency of output mesh (most for debugging/testing")
+ ("verbose,v", "Output more information about what is going on")
+ ("help,h", "write help message");
+
+ // Options not shown to the user
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(), "Input file")
+ ;
+
+ po::options_description cmdline("Generate mesh from surface file");
+ cmdline.add(visible).add(hidden);
+
+ //Command line positional arguments
+ po::positional_options_description p;
+ p.add("input-file", 1);
+
+ // parse command line
+ po::store(po::command_line_parser(argc, argv)
+ .options(cmdline)
+ .positional(p).run(), vm);
+
+ // Print help message if requested
+ // (before notify to avoid error messages if only --help is given)
+ if (vm.count("help"))
+ {
+ std::cout << visible << std::endl;
+ exit(EXIT_SUCCESS);
+ }
+
+ po::notify(vm);
+
+}
+} //end anonymous namespace
+//-----------------------------------------------------------------------------
+int main(int argc, char** argv)
+{
+ // Ensure Dolfin initializes MPI correctly.
+ #ifdef HAS_MPI
+ dolfin::SubSystemsManager::init_mpi();
+ #endif
+
+ po::variables_map vm;
+ handle_commandline(argc, argv, vm);
+
+ if (vm.count("verbose"))
+ dolfin::set_log_level(dolfin::TRACE);
+
+ // Read the infile
+ if (!boost::filesystem::exists(vm["input-file"].as<std::string>()))
+ {
+ std::cerr << "File " << vm["input-file"].as<std::string>() << "does not exist" << std::endl;
+ exit(1);
+ }
+
+
+ std::shared_ptr<mshr::Surface3D> surf(new mshr::Surface3D(vm["input-file"].as<std::string>()));
+ surf->degenerate_tolerance = vm["degenerate_tolerance"].as<double>();
+
+ // Operations that disable mesh generation
+ if (vm.count("polyout") || vm.count("polystats"))
+ {
+ mshr::CSGCGALDomain3D domain(surf);
+
+ if (vm.count("polyout"))
+ {
+ std::string extension = boost::filesystem::extension(vm["polyout"].as<std::string>());
+
+ if (extension != ".off")
+ {
+ std::cerr << "Unknown file type: " << extension << std::endl;
+ exit(1);
+ }
+
+ domain.save_off(vm["polyout"].as<std::string>());
+ }
+
+ if (vm.count("polystats"))
+ std::cout << domain.str(true) << std::endl;
+
+ exit(EXIT_SUCCESS);
+ }
+
+ // Generate the mesh
+ std::shared_ptr<dolfin::Mesh> m = mshr::generate_mesh(surf,
+ vm["resolution"].as<double>(),
+ vm["backend"].as<std::string>());
+
+ // Output mesh if requested
+ if (vm.count("outfile"))
+ {
+ dolfin::File f(vm["outfile"].as<std::string>());
+ f << *m;
+ }
+
+ if (vm.count("stats"))
+ print_mesh_statistics(*m);
+
+ if (vm.count("check-mesh"))
+ {
+ if (!mshr::DolfinMeshUtils::check_mesh(*m))
+ {
+ std::cout << " Error: Mesh check failed" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#.rst:
+# FindTetGen
+# --------
+#
+# Find TetGen library
+#
+# Find the TetGen includes and library This module defines
+#
+# ::
+#
+# TETGEN_INCLUDE_DIRS, where to find triangle.h.
+# TETGEN_LIBRARIES, libraries to link against to use triangle.
+# TETGEN_FOUND, If false, do not try to use TETGEN.
+#
+#
+#
+#
+#
+#=============================================================================
+
+find_path(TETGEN_INCLUDE_DIR tetgen.h
+ DOC "The TetGen include directory")
+
+set(TETGEN_NAMES ${TETGEN_NAMES} libtetgen tetgen tet)
+find_library(TETGEN_LIBRARY NAMES ${TETGEN_NAMES}
+ DOC "The TetGen library")
+
+# handle the QUIETLY and REQUIRED arguments and set TETGEN_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(TETGEN
+ REQUIRED_VARS TETGEN_LIBRARY
+ TETGEN_INCLUDE_DIR
+ VERSION_VAR TETGEN_VERSION_STRING)
+
+if(TETGEN_FOUND)
+ set( TETGEN_LIBRARIES ${TETGEN_LIBRARY} )
+ set( TETGEN_INCLUDE_DIRS ${TETGEN_INCLUDE_DIR} )
+ add_definitions(-DTETLIBRARY)
+endif()
+
+mark_as_advanced(TETGEN_INCLUDE_DIR TETGEN_LIBRARY)
--- /dev/null
+# Copyright (C) 2015 Anders Logg
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+from dolfin import *
+from mshr import *
+
+# Create geometry
+s1 = Sphere(Point(0, 0, 0), 1.4)
+b1 = Box(Point(-1, -1, -1), Point(1, 1, 1))
+c1 = Cylinder(Point(-2, 0, 0), Point(2, 0, 0), 0.8, 0.8)
+c2 = Cylinder(Point(0, -2, 0), Point(0, 2, 0), 0.8, 0.8)
+c3 = Cylinder(Point(0, 0, -2), Point(0, 0, 2), 0.8, 0.8)
+
+geometry = s1*b1 - (c1 + c2 + c3)
+
+# Create mesh
+mesh = generate_mesh(geometry, 64)
+
+# Save to file and plot
+File("classic.pvd") << mesh
+
+plot(mesh)
+
+# import matplotlib.pyplot as plt
+# plt.show()
--- /dev/null
+# Copyright (C) 2012-2014 Benjamin Kehlet
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin
+from mshr import *
+
+#dolfin.set_log_level(dolfin.TRACE)
+
+# Define 2D geometry
+domain = Rectangle(dolfin.Point(0., 0.), dolfin.Point(5., 5.)) \
+ - Rectangle(dolfin.Point(2., 1.25), dolfin.Point(3., 1.75)) \
+ - Circle(dolfin.Point(1, 4), .25) \
+ - Circle(dolfin.Point(4, 4), .25)
+domain.set_subdomain(1, Rectangle(dolfin.Point(1., 1.), dolfin.Point(4., 3.)))
+domain.set_subdomain(2, Rectangle(dolfin.Point(2., 2.), dolfin.Point(3., 4.)))
+
+print("Verbose output of 2D geometry:")
+dolfin.info(domain, True)
+
+# Generate and plot mesh
+mesh2d = generate_mesh(domain, 45)
+print(mesh2d)
+
+dolfin.plot(mesh2d, "2D mesh")
+
+# Convert subdomains to mesh function for plotting
+mf = dolfin.MeshFunction("size_t", mesh2d, 2, mesh2d.domains())
+dolfin.plot(mf, "Subdomains")
+
+# import matplotlib.pyplot as plt
+# plt.show()
--- /dev/null
+# Copyright (C) 2015 Anders Logg
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+from dolfin import *
+from mshr import *
+from math import pi, sin, cos, sqrt
+
+# Parameters
+R = 1.02
+r = 0.4
+t = 10
+x = R*cos(float(t) / 180 * pi)
+y = 0
+z = R*sin(t)
+
+# Create geometry
+s1 = Sphere(Point(0, 0, 0), 1)
+s2 = Sphere(Point(x, y, z), r)
+b1 = Box(Point(-2, -2, -0.03), Point(2, 2, 0.03))
+geometry = s1 - s2 - b1
+
+# Create mesh
+mesh = generate_mesh(geometry, 32)
+
+# Save to file and plot
+File("deathstar.pvd") << mesh
+
+plot(mesh)
+
+# import matplotlib.pyplot as plt
+# plt.show()
--- /dev/null
+# Copyright (C) 2015 Benjamin Kehlet
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+# This demo illustrates how a 2D geometry can be extruded to 3D.
+
+from dolfin import *
+import mshr
+
+g2d = mshr.Circle(Point(0,0), 1.2) + mshr.Circle(Point(0, 1.2), 1.2)
+
+# Any simple 2D geometry can be extruded to 3D
+g3d = mshr.Extrude2D(g2d,
+ .2) # The z "thickness"
+
+m = mshr.generate_mesh(g3d, 15)
+plot(m, interactive=True)
--- /dev/null
+# Copyright (C) 2014 Benjamin Kehlet
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import dolfin
+from mshr import *
+
+# Define 3D geometry
+sphere = Sphere(dolfin.Point(0, 0, 0), 0.5)
+cone = Cylinder(dolfin.Point(0, 0, 0), dolfin.Point(0, 0, -1), .35, .1)
+
+geometry = cone + sphere
+
+# Geometry surfaces can be saved to off files
+# which can be viewed by eg. MeshLab
+meshing_domain = CSGCGALDomain3D(geometry)
+meshing_domain.remove_degenerate_facets(1e-12)
+meshing_domain.save("icecream.off")
+
+# Test printing
+dolfin.info("\nCompact output of 3D geometry:")
+dolfin.info(geometry)
+dolfin.info("\nVerbose output of 3D geometry:")
+dolfin.info(geometry, True)
+
+# Generate and plot mesh
+m = generate_mesh(geometry, 16, "cgal")
+print(m)
+
+dolfin.plot(m, "3D mesh")
+
+# import matplotlib.pyplot as plt
+# plt.show()
+
--- /dev/null
+# Copyright (C) 2014 Benjamin Kehlet
+#
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin
+from mshr import *
+
+#dolfin.set_log_level(dolfin.TRACE)
+
+# Define 2D geometry
+domain = Rectangle(dolfin.Point(0., 0.), dolfin.Point(1., 1.)) - Circle(dolfin.Point(0.0, 0.0), .35)
+domain.set_subdomain(1, Rectangle(dolfin.Point(.05, .05), dolfin.Point(.95, .95)))
+domain.set_subdomain(2, Circle(dolfin.Point(0, 0), .45))
+domain.set_subdomain(3, Circle(dolfin.Point(0,0), .6))
+
+# Generate and plot mesh
+mesh2d = generate_mesh(domain, 45)
+dolfin.plot(mesh2d, "2D mesh")
+
+# Convert subdomains to mesh function for plotting
+mf = dolfin.MeshFunction("size_t", mesh2d, 2, mesh2d.domains())
+
+dolfin.plot(mf, "Subdomains")
+
+# import matplotlib.pyplot as plt
+# plt.show()
+
--- /dev/null
+# Copyright (C) 2014 Anders Logg
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+from mshr import *
+from dolfin import *
+
+# Set parameters
+r = 0.125
+R = 0.5
+w = 0.3
+h = 0.025
+rotate_blades = True
+include_tip = False
+extra_rotation = True # only applied to inner mesh
+resolution = 4
+
+# Define geometries
+sphere = Sphere(Point(0, 0, 0), 2*R)
+geometry_inside = CSGGeometries.propeller(r, R, w, h, rotate_blades, include_tip)
+geometry_outside = sphere - geometry_inside
+
+# Generate meshes
+mesh_inside = generate_mesh(geometry_inside, resolution)
+mesh_outside = generate_mesh(geometry_outside, resolution)
+
+# Rotate blades
+if extra_rotation:
+ print("Rotating blades...")
+ c = mesh_inside.coordinates()
+ for i, (x, y, z) in enumerate(c):
+
+ # Compute distance to axis
+ _r = sqrt(x**2 + y**2)
+
+ # Compute rotation angle
+ v = -2*max(0, _r - r)
+
+ # Rotate blades
+ xx = x; yy = y; zz = z;
+ if x > 0 and abs(y) < 5*h:
+ yy = cos(v)*y - sin(v)*z
+ zz = sin(v)*y + cos(v)*z
+ elif x < 0 and abs(y) < 5*h:
+ yy = cos(v)*y + sin(v)*z
+ zz = -sin(v)*y + cos(v)*z
+ elif y > 0 and abs(x) < 5*h:
+ xx = cos(v)*x + sin(v)*z
+ zz = -sin(v)*x + cos(v)*z
+ elif y < 0 and abs(x) < 5*h:
+ xx = cos(v)*x - sin(v)*z
+ zz = sin(v)*x + cos(v)*z
+
+ # Store coordinates
+ c[i][0] = xx
+ c[i][1] = yy
+ c[i][2] = zz
+
+# Report size of meshes
+print("Mesh of propeller inside: %d cells" % mesh_inside.num_cells())
+print("Mesh of propeller outside: %d cells" % mesh_outside.num_cells())
+
+# Save meshes to file
+File("propeller_inside.xml.gz") << mesh_inside
+File("propeller_outside.xml.gz") << mesh_outside
+
+# Plot mesh
+plot(mesh_inside)
+plot(mesh_outside)
+
+# import matplotlib.pyplot as plt
+# plt.show()
--- /dev/null
+from __future__ import print_function
+import mshr
+import dolfin
+
+TOLERANCE = 1e-10
+
+# This geometry generates a lot of degenerate facets
+cone = mshr.Cylinder(dolfin.Point(-1.0, 1.0, 1.0), dolfin.Point(1.0, -1.0, -1.0), .5, .5)
+cyl = mshr.Cone(dolfin.Point(1.0, -1.0, 1.0), dolfin.Point(-1.0, 1.0, -1.0), .5)
+geometry = cone + cyl;
+
+polyhedral_domain = mshr.CSGCGALDomain3D(geometry)
+
+print("Degenerate facets after boolean operation: {0}".format(polyhedral_domain.num_degenerate_facets(TOLERANCE)))
+polyhedral_domain.remove_degenerate_facets(TOLERANCE)
+
+dolfin.info(polyhedral_domain, True)
+
--- /dev/null
+# Copyright (C) 2012-2014 Benjamin Kehlet
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+import dolfin
+from mshr import *
+
+# Define 3D geometry
+box = Box(dolfin.Point(0, 0, 0), dolfin.Point(1, 1, 1))
+sphere = Sphere(dolfin.Point(0, 0, 0), 0.3)
+cylinder = Cylinder(dolfin.Point(0, 0, -1), dolfin.Point(0, 0, 1), 1., .5)
+
+domain = box + cylinder - sphere
+
+# Test printing
+dolfin.info("\nCompact output of 3D geometry:")
+dolfin.info(domain)
+dolfin.info("\nVerbose output of 3D geometry:")
+dolfin.info(domain, True)
+
+# Creating a mesh generator object gives access to parameters of the
+# meshing backend
+generator = CSGCGALMeshGenerator3D()
+generator.parameters["edge_size"] = 0.025
+generator.parameters["facet_angle"] = 25.0
+generator.parameters["facet_size"] = 0.05
+
+# Invoke the mesh generator
+mesh = generator.generate(CSGCGALDomain3D(domain))
+
+dolfin.plot(mesh, "3D mesh")
+
+# import matplotlib.pyplot as plt
+# plt.show()
--- /dev/null
+# Doxyfile 1.8.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed
+# in front of the TAG it is preceding .
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = "mshr"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 1.4
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Mesh generation in FEniCS"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian,
+# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic,
+# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields or simple typedef fields will be shown
+# inline in the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO (the default), structs, classes, and unions are shown on a separate
+# page (for HTML and Man pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can
+# be an expensive process and often the same symbol appear multiple times in
+# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too
+# small doxygen will become slower. If the cache is too large, memory is wasted.
+# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid
+# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536
+# symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ../include/mshr
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be ignored.
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = NO
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript
+# pieces of code that will be used on startup of the MathJax code.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search
+# engine library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4 will be used.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images
+# or other source files which should be copied to the LaTeX output directory.
+# Note that the files will be copied as-is; there are no commands or markers
+# available.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files
+# that can be used to generate PDF.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it. If left blank docbook will be used as the default path.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed
+# in the related pages index. If set to NO, only the current project's
+# pages will be listed.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# manageable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
--- /dev/null
+# This script generates a set of .md (MarkDown) files
+# from xml files generated by doxygen.
+
+# The script does two sweeps.
+# First all xml are parsed and stored in dictionaries
+# with their id as key.
+# The second sweep generates one page (a markdown file) pr class
+# and collects the information for main page which is generated
+# at the end.
+
+import sys, os
+from xml.etree import ElementTree
+import subprocess
+
+input_dir = os.path.abspath(sys.argv[1])
+output_dir = os.path.abspath(sys.argv[2])
+
+if not os.path.exists(output_dir) :
+ os.mkdir(output_dir)
+
+if not os.path.exists(os.path.join(output_dir, "API")) :
+ os.mkdir(os.path.join(output_dir, "API"))
+
+
+# searches through the detaileddescription section
+# for parameter documentation with the same name
+# and returns it
+def get_parameter_documentation(functiondefinition, declname) :
+ details = functiondefinition.find("detaileddescription")
+ para = details.find("para")
+ if para is not None :
+ paralist = para.find("parameterlist")
+ if paralist is not None :
+ items = paralist.findall("parameteritem")
+ for parameteritem in items :
+ name = parameteritem.find("parameternamelist").find("parametername").text.strip()
+
+ if name == declname.strip() :
+ # We got the right parameter
+ description = parameteritem.find("parameterdescription")
+ if description is not None :
+ para = description.find("para")
+ if para is not None and para.text is not None:
+ return para.text.strip()
+ else :
+ return ""
+ return ""
+
+def get_extra_data(classdefinition) :
+ details = classdefinition.find("detaileddescription")
+ paragraphs = details.findall("para")
+ for para in paragraphs :
+ text = para.text.strip()
+ if text.startswith("{") and text.endswith("}") :
+ return eval(text)
+ return {}
+
+print("Reading files from %s" % input_dir)
+
+# save all the classes as dictionary with doxygens id as key.
+cls = {}
+
+# First sweep
+for filename in os.listdir(input_dir) :
+ if not os.path.isfile(os.path.join(input_dir, filename)) :
+ continue
+
+ if not filename.endswith(".xml") :
+ continue
+
+ # Only classes are documented now
+ if not filename.startswith("class") :
+ continue
+
+ # print("Processing %s" % filename)
+
+ root = ElementTree.parse(os.path.join(input_dir, filename)).getroot()
+ assert root.tag == "doxygen"
+ assert len(root) == 1
+
+ class_def = root.find("compounddef")
+ assert not cls.has_key(class_def.attrib["id"])
+
+ class_name = class_def.find("compoundname").text
+ outfilename_base = class_name if not class_name.startswith("mshr::") else class_name[6:]
+ briefdescription = class_def.find("briefdescription")
+ # Description should at most one paragraph
+ assert (len(briefdescription) <= 1)
+ briefdescription = briefdescription.find("para").text if briefdescription.find("para") is not None else ""
+
+ data = {'filename' : outfilename_base,
+ 'description' : briefdescription.strip() }
+
+ extra_data = get_extra_data(class_def)
+ data.update(extra_data)
+
+ cls[class_def.attrib["id"]] = (class_def, data)
+
+longest_class_name = max([ len(c[0].find("compoundname").text) for k, c in cls.iteritems()])
+longest_description = max([ len(c[1]["description"]) for k, c in cls.iteritems()])
+
+primitives2d = []
+primitives3d = []
+operators = []
+others = []
+
+
+# Second sweep
+for k,c in cls.iteritems() :
+ theclass = c[0]
+ classname = theclass.find("compoundname").text
+ classid = theclass.attrib["id"]
+ print "Processing "+classname
+
+ classpage = []
+ category = others
+
+ # inheritance
+ # also decides which category the class belong to
+ inheritance = theclass.find("inheritancegraph")
+ if inheritance is not None :
+ inheritancenodes = {}
+ nodes = inheritance.findall("node")
+
+ # collect all inheritance nodes in a dictionary with the refid (which is not the same
+ # as the class id as key
+ myid = False
+ for x in nodes :
+ inheritancenodes[x.attrib["id"]] = x
+ link = x.find("link")
+
+ # save the refid to self
+ if link is not None and link.attrib["refid"] == classid :
+ myid = x.attrib["id"]
+
+ # traverse the tree from self through all super classes
+ current = inheritancenodes[myid]
+ current = inheritancenodes[current.find("childnode").attrib["refid"]]
+ inheritance_list = []
+ while current is not None :
+ label = current.find("label").text
+ if label == "mshr::CSGPrimitive2D" : category = primitives2d
+ elif label == "mshr::CSGPrimitive3D" : category = primitives3d
+ elif label == "mshr::CSGOperator" : category = operators
+
+ ref = current.find("link")
+
+ if ref is not None and cls.has_key(ref.attrib["refid"]) :
+ inheritance_list.append("[%s](%s)" % (current.find("label").text.strip(),
+ cls[ref.attrib["refid"]][1]["filename"]))
+ else :
+ inheritance_list.append(x.find("label").text.strip())
+
+ child = current.find("childnode")
+ if child is not None :
+ current = inheritancenodes[child.attrib["refid"]]
+ else :
+ current = None
+
+
+ inheritance_list.reverse()
+ classpage.append("_" + " > ".join(inheritance_list) + "_")
+ classpage.append("\n\n")
+
+ icon_txt = " |"
+ if c[1].has_key("small-icon") :
+ icon_txt = "|" % (classname, c[1]["small-icon"])
+
+ print "Appending to category"
+ category.append("%s[%s](API/%s)|%s|\n" % (icon_txt,
+ classname.ljust(longest_class_name),
+ c[1]["filename"],
+ c[1]["description"].ljust(longest_description)))
+
+
+
+ classpage.append("# %s\n\n" % (classname))
+ classpage.append("_%s_\n\n" % c[1]["description"])
+
+ classpage.append("#### Public functions\n\n")
+ for member in c[0].findall("sectiondef") :
+
+ # Only public functions are considered part of the public API for now
+ if member.attrib["kind"] != "public-func" :
+ continue
+
+ for definition in member.findall("memberdef") :
+ membername = definition.find("name").text
+
+ argsstr = definition.find("argsstring").text
+ description = definition.find("briefdescription").find("para")
+ description = description.text.strip() if description is not None else ""
+
+ returntype = definition.find("type").text
+ if returntype is not None and len(returntype) > 0 :
+ returntype = returntype+" "
+ if returntype is None :
+ returntype = ""
+
+ # name
+ classpage.append("%s**%s**%s\n" % (returntype, membername, argsstr))
+
+ # description
+ if description :
+ classpage.append("> _%s_\n" % description)
+
+ classpage.append("\n")
+
+ param_table = []
+
+ # table of parameters
+ for param in definition.findall("param") :
+ declname = param.find("declname").text
+ paramtype = param.find("type").text
+ if paramtype is None :
+ paramtype = ""
+
+ desc = get_parameter_documentation(definition, declname)
+ param_table.append( (declname, paramtype, desc) )
+
+ if len(param_table) > 0 :
+ longest_param_name = max([len(x[0]) for x in param_table])
+ longest_param_type = max([len(x[1]) for x in param_table])
+ longest_param_desc = max([len(x[2]) for x in param_table])
+
+ longest_param_name = max(longest_param_name, len("Parameters"))
+
+ classpage.append("> %s|%s|%s\n" % ("Parameters".ljust(longest_param_name),
+ " "*longest_param_type,
+ " "*longest_param_desc))
+ classpage.append("> %s|%s|%s\n" % ("-"*longest_param_name,
+ "-"*longest_param_type,
+ "-"*longest_param_desc))
+
+ for p in param_table :
+ classpage.append("> %s|%s|%s\n" % (p[0].ljust(longest_param_name),
+ p[1].ljust(longest_param_type),
+ p[2].ljust(longest_param_desc)))
+
+ classpage.append("\n")
+
+
+ with open(os.path.join(output_dir, "API", c[1]["filename"]+".md"), "w") as f:
+ f.write("".join(classpage))
+ f.close()
+
+
+
+# Create the main page for
+mainpage = ["""
+
+# API reference
+
+"""]
+
+mainpage.append("- |%s|%s|\n" % ("2D primitives".ljust(longest_class_name),
+ "Description".ljust(longest_description)))
+
+mainpage.append("--|%s|%s|\n" % ( ":".ljust(longest_class_name, "-"),
+ ":".ljust(longest_description, "-")))
+
+
+################# Append 2D primitives
+#mainpage.append("%s|%s|\n" % ("**2D primitives**".ljust(longest_class_name),
+# "".ljust(longest_description)))
+
+# mainpage.append("%s|%s|\n" % ( ":".ljust(longest_class_name, "-"),
+# ":".ljust(longest_description, "-")))
+
+mainpage.append("".join(primitives2d))
+#mainpage.append("\n\n")
+
+################ Append 3D primitives
+mainpage.append("- |%s|%s|\n" % ("**3D primitives**".ljust(longest_class_name),
+ " "*longest_description))
+
+# mainpage.append("%s|%s|\n" % ( ":".ljust(longest_class_name, "-"),
+# ":".ljust(longest_description, "-")))
+
+mainpage.append("".join(primitives3d))
+#mainpage.append("\n\n")
+
+############### Append operators
+mainpage.append("- |%s|%s|\n" % ("**Operators**".ljust(longest_class_name),
+ " "*longest_description))
+
+# mainpage.append("%s|%s|\n" % ( ":".ljust(longest_class_name, "-"),
+# ":".ljust(longest_description, "-")))
+
+mainpage.append("".join(operators))
+# mainpage.append("\n\n")
+
+############### Append other classes
+mainpage.append("- |%s|%s|\n" % ("**Other classes**".ljust(longest_class_name),
+ " "*longest_description))
+
+# mainpage.append("%s|%s|\n" % ( ":".ljust(longest_class_name, "-"),
+# ":".ljust(longest_description, "-")))
+
+mainpage.append("".join(others))
+mainpage.append("\n\n")
+
+# Read the current revision from git
+revision_info = subprocess.check_output(["git", "log", "--pretty=format:'%ad %H'", "-1"]).strip("'")
+mainpage.append("_Generated from revision: {}. Please don't edit these files manually._\n".format(revision_info))
+
+
+
+with open(os.path.join(output_dir, "API.md"), "w") as f:
+ f.write("".join(mainpage))
+ f.close()
+
--- /dev/null
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mshr.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mshr.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/mshr"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mshr"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
--- /dev/null
+====================
+Sphinx documentation
+====================
+
+mshr is documented using Sphinx and reStructured text. The
+documentation is hosted at http://fenics-mshr.readthedocs.org/. The
+online documentation is automatically updated upon pushes to the mshr
+master branch.
+
+
+Building the documentation locally
+==================================
+
+The HTML documentation can be built locally using::
+
+ make html
+
+In order for changes in the docstring to be propagated to the html
+pages, mshr must be installed anew. In other words, sphinx reads the
+docstrings from the installed code, not the source code.
--- /dev/null
+../../../ChangeLog.rst
\ No newline at end of file
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# mshr documentation build configuration file, created by
+# sphinx-quickstart on Thu Aug 11 13:48:10 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+from __future__ import print_function
+import sys
+import os
+
+
+def grep_ver(verstr, lines):
+ """Based on a loose criterion find a string of type::
+
+ set(MSHR_VERSION_verstr number)
+
+ or::
+
+ set(MSHR_VERSION_verstr "number")
+
+ in provided iterable of strings and return `number`."""
+
+ left = "set(MSHR_VERSION_{}".format(verstr)
+ right = ")"
+
+ line, = [l for l in lines if left in l]
+ line = line.strip() # strip whitespace
+
+ begin, sep, end = line.partition(left)
+ assert len(begin) == 0 and sep == left
+ line = end
+
+ begin, sep, end = line.partition(right)
+ assert len(end) == 0 and sep == right
+ line = begin
+
+ line = line.strip(' \'"') # strip whitespace and quotes
+ int(line) # check integer
+
+ return line
+
+
+def get_mshr_version():
+ "Parse `CMakeLists.txt` for mshr version and return as string"
+ filepath = os.path.join(os.path.pardir, os.path.pardir, os.path.pardir,
+ "CMakeLists.txt")
+ with open(filepath, "r") as f:
+ lines = f.readlines()
+ major = grep_ver("MAJOR", lines)
+ minor = grep_ver("MINOR", lines)
+ micro = grep_ver("MICRO", lines)
+ release = grep_ver("RELEASE", lines)
+ if release == "1":
+ ver_str = ".".join((major, minor, micro))
+ else:
+ assert release == "0"
+ ver_str = ".".join((major, minor, micro, "dev0"))
+ print("Detected mshr version '{}'".format(ver_str))
+ return ver_str
+
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.viewcode',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'mshr'
+copyright = u'2016, FEniCS Project'
+author = u'FEniCS Project'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+mshr_version = get_mshr_version()
+# The short X.Y version.
+version = mshr_version
+# The full version, including alpha/beta/rc tags.
+release = mshr_version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'mshrdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'mshr.tex', u'mshr Documentation',
+ u'FEniCS Project', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'mshr', u'mshr Documentation',
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'mshr', u'mshr Documentation',
+ author, 'mshr', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
--- /dev/null
+mshr documentation
+==================
+
+Mshr manual is located on `Bitbucket wiki
+<https://bitbucket.org/fenics-project/mshr/wiki/Home>`_.
+It is waiting for porting to Read the Docs. Contributions
+are welcome and first best discussed on `FEniCS Slack
+#documentation channel
+<https://fenicsproject.slack.com/messages/C1AGB4402/>`_,
+see `instruction for joining <https://fenicsproject.org/community/>`_.
+
+Automatically generated API documentation is not available yet.
+The approach from DOLFIN could possibly be ported to mshr too.
+Again, contributions are welcome.
+
+Contents:
+
+.. toctree::
+ :maxdepth: 1
+
+ ChangeLog
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
--- /dev/null
+#!/usr/bin/env bash
+#
+# Developer script for configure + build + rebuild.
+#
+# Notes:
+#
+# - This script is what most developers use to build/rebuild this package.
+# - This script works for both CMake and distutils based packages.
+# - If this script is updated in one package, please propagate to the others!
+#
+# Environment variables:
+#
+# - $PROCS : controls number of processes to use for build
+# : defaults to 6
+# - $FENICS_PYTHON_EXECUTABLE : name of python executable
+# : defaults to "python"
+# - $FENICS_INSTALL_PREFIX : path to FEniCS installation prefix
+# : defaults to "${HOME}/opt/<branchname>"
+
+# Exit on first error
+set -e
+
+# Get branch name
+BRANCH=`(git symbolic-ref --short HEAD 2> /dev/null || git describe HEAD) | sed s:/:.:g`
+echo "On branch '${BRANCH}'."
+
+# Get installation prefix
+: ${FENICS_INSTALL_PREFIX:="${HOME}/opt/fenics/${BRANCH}"}
+echo "Installation prefix set to '${FENICS_INSTALL_PREFIX}'."
+
+# Get Python executable and version
+: ${FENICS_PYTHON_EXECUTABLE:=python}
+FENICS_PYTHON_VERSION=$(${FENICS_PYTHON_EXECUTABLE} -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
+echo "Python executable and version set to '${FENICS_PYTHON_EXECUTABLE} ${FENICS_PYTHON_VERSION}'."
+
+# Get number of processes to use for build
+: ${PROCS:=6}
+
+
+# Build and install distutils based FEniCS package
+if [ -e setup.py ]; then
+ ${FENICS_PYTHON_EXECUTABLE} setup.py build
+ ${FENICS_PYTHON_EXECUTABLE} setup.py install --prefix=${FENICS_INSTALL_PREFIX}
+fi
+
+
+# Build and install dolfin
+if [ -e CMakeLists.txt ]; then
+ # Set build directory
+ if [ "${BRANCH}" = "master" ]; then
+ BUILD_DIR=build.${BRANCH}
+ elif [ "${BRANCH}" = "next" ]; then
+ BUILD_DIR=build.${BRANCH}
+ else
+ BUILD_DIR="build.wip" # use for all other branches to save disk space
+ fi
+
+ # Configure
+ CMAKE_EXTRA_ARGS=$@
+ mkdir -p ${BUILD_DIR}
+ cd ${BUILD_DIR}
+ time cmake -DCMAKE_INSTALL_PREFIX=${FENICS_INSTALL_PREFIX} \
+ -DDOLFIN_ENABLE_TESTING=true \
+ -DDOLFIN_ENABLE_BENCHMARKS=true \
+ -DCMAKE_BUILD_TYPE=Developer \
+ -DDOLFIN_DEPRECATION_ERROR=false \
+ ${CMAKE_EXTRA_ARGS} \
+ ..
+
+ # Build and install
+ time make -j ${PROCS} -k && make install -j ${PROCS}
+fi
+
+
+# Write config file
+CONFIG_FILE="${FENICS_INSTALL_PREFIX}/fenics.conf"
+rm -f ${CONFIG_FILE}
+cat << EOF > ${CONFIG_FILE}
+# FEniCS configuration file created by fenics-dev-install.sh on $(date)
+export FENICS_INSTALL_PREFIX=${FENICS_INSTALL_PREFIX}
+export FENICS_PYTHON_EXECUTABLE=${FENICS_PYTHON_EXECUTABLE}
+export FENICS_PYTHON_VERSION=${FENICS_PYTHON_VERSION}
+
+# Source FEniCS dependencies if found
+FENICS_DEPS_CONF=\${HOME}/opt/fenics/fenics.deps
+if [ -e \${FENICS_DEPS_CONF} ]; then
+ source \${FENICS_DEPS_CONF}
+fi
+
+# Common Unix variables
+export LD_LIBRARY_PATH=\${FENICS_INSTALL_PREFIX}/lib:\${LD_LIBRARY_PATH}
+export PATH=\${FENICS_INSTALL_PREFIX}/bin:\${PATH}
+export PKG_CONFIG_PATH=\${FENICS_INSTALL_PREFIX}/pkgconfig:\${PKG_CONFIG_PATH}
+export PYTHONPATH=\${FENICS_INSTALL_PREFIX}/lib/python${FENICS_PYTHON_VERSION}/site-packages:\${PYTHONPATH}
+export MANPATH=\${FENICS_INSTALL_PREFIX}/share/man:\${MANPATH}
+
+# Set Instant cache modules separately for each install
+export INSTANT_CACHE_DIR=\${FENICS_INSTALL_PREFIX}/cache/instant
+
+# CMake search path
+export CMAKE_PREFIX_PATH=\${FENICS_INSTALL_PREFIX}:\${CMAKE_PREFIX_PATH}
+EOF
+if [ $(uname) = "Darwin" ]; then
+ cat << EOF >> $CONFIG_FILE
+
+# Mac specific path
+export DYLD_FALLBACK_LIBRARY_PATH=\${FENICS_INSTALL_PREFIX}/lib:\${DYLD_FALLBACK_LIBRARY_PATH}
+EOF
+fi
+
+# Print information
+echo
+echo "- Installed branch '${BRANCH}' to ${FENICS_INSTALL_PREFIX}."
+echo
+echo "- Config file written to ${CONFIG_FILE}"
+echo " (source this file)."
+echo
--- /dev/null
+#include <mshr/MeshGenerator.h>
+#include <mshr/CSGOperators.h>
+#include <mshr/CSGPrimitives2D.h>
+#include <mshr/CSGPrimitives3D.h>
+#include <mshr/CSGCGALMeshGenerator2D.h>
+#include <mshr/CSGCGALMeshGenerator3D.h>
+#include <mshr/CSGCGALDomain3D.h>
+#include <mshr/CSGGeometries3D.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGPrimitive.h>
+#include <mshr/DolfinMeshUtils.h>
--- /dev/null
+// Copyright (C) 2016 Lars Magnus Valnes
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __ASC_FILE_READER_H // ??
+#define __ASC_FILE_READER_H // ??
+
+#include <string>
+#include <vector>
+#include <array>
+
+namespace mshr
+{
+
+class ASCFileReader
+{
+ public:
+ /// @brief Read and parse a triangular 3d surface off file
+ /// @param filename The file to read
+ /// @param vertices A vector of points to be read into
+ /// @param facets A vector of facets given as indices to the vertex array.
+ static void read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets);
+
+ static void write(const std::string filename,
+ const std::vector<std::array<double, 3> >& vertices,
+ const std::vector<std::array<std::size_t, 3> >& facets);
+
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2013-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_CSGCGAL_DOMAIN2D_H
+#define __MSHR_CSGCGAL_DOMAIN2D_H
+
+#include <mshr/CSGGeometry.h>
+
+#include <dolfin/geometry/Point.h>
+#include <memory>
+
+
+namespace mshr
+{
+// Forward declaration
+struct CSGCGALDomain2DImpl;
+
+/// @brief Polygonal meshing domain
+///
+/// Represents the polygonal meshing domain to be sent to the mesh
+/// generator. It uses CGAL's Polygon_2 as backend and utilizied CGAL's
+/// 2D Regularized Boolean Set-Operations package.
+class CSGCGALDomain2D : public dolfin::Variable
+{
+ public:
+ /// @brief Create empty polygon
+ CSGCGALDomain2D();
+
+ /// @brief Construct polygon from Dolfin CSG geometry
+ CSGCGALDomain2D(
+ std::shared_ptr<const CSGGeometry> geometry,
+ double segment_granularity
+ );
+
+ /// @brief Destructor
+ ~CSGCGALDomain2D();
+
+ /// @brief Copy constructor
+ CSGCGALDomain2D(const CSGCGALDomain2D &other);
+
+ /// @brief Assignment operator
+ CSGCGALDomain2D &operator=(const CSGCGALDomain2D &other);
+
+ /// @brief Boolean join
+ void join_inplace(const CSGCGALDomain2D& other);
+
+ /// @brief Boolean intersection
+ void intersect_inplace(const CSGCGALDomain2D& other,
+ double truncate_tolerance=-1.);
+
+ /// @brief Boolean difference
+ void difference_inplace(const CSGCGALDomain2D& other);
+
+ /// @brief Determine if point is inside the polygon.
+ bool point_in_domain(dolfin::Point p) const;
+
+ /// @brief Compute the radius of the minimum bounding circle.
+ double compute_boundingcircle_radius() const;
+
+ std::size_t num_polygons() const;
+ double shortest_edge() const;
+
+ std::vector<dolfin::Point> get_outer_polygon(std::size_t i) const;
+
+ /// @brief get one point per hole, strictly inside the hole.
+ /// @param holes the returned points
+ void get_points_in_holes(std::vector<dolfin::Point>& holes) const;
+
+ /// @brief Informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ static
+ std::pair<std::vector<dolfin::Point>,
+ std::vector<std::pair<std::size_t, std::size_t>>>
+ compute_pslg(const std::vector<std::pair<std::size_t, CSGCGALDomain2D>>& domains);
+
+ private:
+ std::unique_ptr<CSGCGALDomain2DImpl> impl;
+};
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2013-2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_CSGCGAL_DOMAIN3D_H
+#define __MSHR_CSGCGAL_DOMAIN3D_H
+
+#include <mshr/CSGPrimitives3D.h>
+#include <dolfin/geometry/Point.h>
+
+#include <memory>
+#include <array>
+
+namespace mshr
+{
+
+ // Forward declaration
+ struct CSGCGALDomain3DImpl;
+ struct CSGCGALDomain3DQueryStructureImpl;
+
+class CSGCGALDomain3DQueryStructure
+{
+ public:
+ CSGCGALDomain3DQueryStructure(std::unique_ptr<CSGCGALDomain3DQueryStructureImpl> impl);
+ ~CSGCGALDomain3DQueryStructure();
+
+ std::unique_ptr<CSGCGALDomain3DQueryStructureImpl> impl;
+};
+
+/// @brief A polyhedron meshing domain.
+///
+/// This class represents a polyhedral meshing domain which implements boolean
+/// operations. It uses CGAL Polyhedron_3 as backend and utilizes CGAL
+// Nef_polyhedron for boolean operations.
+class CSGCGALDomain3D : public CSGPrimitive3D
+{
+ public:
+ /// @brief Create empty polyhedron
+ CSGCGALDomain3D();
+
+ /// @brief Construct polyhedron from CSG geometry
+ CSGCGALDomain3D(std::shared_ptr<const mshr::CSGGeometry> csg);
+
+ CSGCGALDomain3D(const std::vector<std::array<double, 3>>& vertices,
+ const std::vector<std::array<std::size_t, 3>>& facets);
+
+ /// @brief Destructor
+ ~CSGCGALDomain3D();
+
+ Type getType() const
+ { return CSGGeometry::TriPolyhedron; }
+
+ /// @brief Insert polyhedron into this object
+ /// Inserted polyhedron should not intersect
+ void insert(const CSGCGALDomain3D& p);
+
+ /// @brief Number of vertices in polyhedron
+ std::size_t num_vertices() const;
+
+ /// @brief Number of facets in polyhedron
+ std::size_t num_facets() const;
+
+ /// @brief Number of halfedges in polyhedron
+ std::size_t num_halfedges() const;
+
+ /// @brief Volume of polyhedron (experimental, use with care)
+ double volume() const;
+
+ /// @brief Is polyhedron (experimental, use with care)
+ bool is_insideout() const;
+
+ /// @brief Count the number of degenerate facets wrt. the given tolerance
+ std::size_t num_degenerate_facets(double threshold) const;
+
+ std::pair<double, double> facet_area_minmax() const;
+
+ /// @brief get length of shortest edge
+ std::pair<double, double> edge_length_range() const;
+
+ /// @brief count edges with squared length shorter than tolerance
+ std::size_t num_short_edges(double tolerance) const;
+
+ /// @brief Test if any facets intersects
+ bool is_selfintersecting(bool verbose=false) const;
+
+ std::size_t remove_selfintersections();
+
+ void normalize();
+
+ /// @brief Save polyhedron to off file
+ /// @param filename Filename to write to
+ void save_off(std::string filename) const;
+
+ void save(std::string filename) const;
+
+ // TODO: Define iterators to be more memory friendly
+
+ /// @brief Output vertices in double precision
+ /// This outputs as a flattened array
+ std::unique_ptr<std::vector<double>> get_vertices() const;
+
+ /// @brief Output facets as indices to the vertices array
+ std::unique_ptr<std::vector<std::size_t>> get_facets() const;
+
+ /// @brief get one point per hole, strictly inside the hole.
+ /// @param holes the returned points
+ /// @param q a query structure returned from get_query_structure()
+ void get_points_in_holes(std::vector<dolfin::Point>& holes,
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> q=std::shared_ptr<CSGCGALDomain3DQueryStructure>()) const;
+
+ /// @brief Attempt to remove degenerate facets.
+ void remove_degenerate_facets(double tolerance);
+
+ /// @brief Attempts to ensure that the preconditions
+ /// for successfull meshing generation are fullfilled.
+ void ensure_meshing_preconditions();
+
+ /// @brief Return data structure that allows fast queries,
+ /// essentially a wrapper for a CGAL AABB tree of the polyhedron triangles.
+ /// When performing queries, the user is responsible for the query structure
+ /// being in sync with the CSGCGALDomain3D object.
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> get_query_structure() const;
+
+ /// Default parameter values
+ static dolfin::Parameters default_parameters()
+ {
+ dolfin::Parameters p("csg_cgal_domain_3d");
+ p.add("remove_degenerate", true);
+ p.add("degenerate_tolerance", 1e-10);
+
+ return p;
+ }
+
+ /// @brief Informal string representation
+ /// @param verbose The verbosity level
+ std::string str(bool verbose) const;
+
+ /// @brief Remove facets, starting from the facets closest to to the given
+ /// point
+ void filter_facets(dolfin::Point start,
+ double threshold,
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> q=std::shared_ptr<CSGCGALDomain3DQueryStructure>());
+
+ void inside_out();
+
+ /// A hole is a connected sequence of boundary edges
+ std::size_t num_holes() const;
+
+ /// Disconnected components are parts of the polyhedron that can not be
+ /// reached through adjacent entities.
+ std::size_t num_disconnected_components() const;
+
+ /// Keep only the n largest disconnected components. Returns the number of
+ /// components removed.
+ std::size_t keep_largest_components(std::size_t n);
+
+ /// get the axis aligned bounding box of the polyhedron, represented as min and
+ /// max points
+ std::pair<dolfin::Point, dolfin::Point> get_aabb() const;
+
+ /// @brief Close and triangulate all holes. Experimental.
+ void close_holes();
+
+ /// @brief Close and triangulate hole. Experimental.
+ bool close_hole(std::size_t hole, std::string method="auto");
+
+ void list_hole(std::size_t hole) const;
+
+ double sharpest_edge() const;
+ std::size_t num_sharp_edges(double tolerance) const;
+
+ void smooth_taubin(std::size_t iterations=3);
+ void smooth_laplacian(double c=1.0);
+
+ /// Do a simple center vertex refinement
+ /// Note: This decreases the triangle quality
+ void refine_center_vertex();
+
+ /// Refine by splitting all edges
+ void refine_edge_split();
+
+ // @brief Reconstruct surface from point set. Experimental
+ std::shared_ptr<CSGCGALDomain3D> reconstruct_surface(double expansion=0.0) const;
+
+ // @brief Isotropic remeshing. Experimental
+ std::shared_ptr<CSGCGALDomain3D> remesh_surface(double edge_length,
+ double sharp_edge_tolerance=60) const;
+
+ /// @brief Return convex hull of vertices as CSGCGALDomain3D object. Experimental
+ std::shared_ptr<CSGCGALDomain3D> convex_hull() const;
+
+ static std::shared_ptr<CSGCGALDomain3D>
+ convex_hull(const std::vector<std::array<double, 3>>& point_set);
+
+ // FIXME: Make this private again
+ // private :
+ std::unique_ptr<CSGCGALDomain3DImpl> impl;
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2012-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+#ifndef __MSHR_CGAL_MESH_GENERATOR2D_H
+#define __MSHR_CGAL_MESH_GENERATOR2D_H
+
+#include <dolfin/common/Variable.h>
+
+#include <vector>
+#include <memory>
+
+// Forward declaration
+namespace dolfin{ class Mesh; }
+
+namespace mshr
+{
+
+ // Forward declaration
+ class CSGCGALDomain2D;
+
+ /// @brief Mesh generator for Constructive Solid Geometry (CSG)
+ /// utilizing CGALs 2D Regularized Boolean Set-Operations
+ class CSGCGALMeshGenerator2D : public dolfin::Variable
+ {
+ public :
+
+ /// @brief Create mesh generator
+ CSGCGALMeshGenerator2D();
+
+ /// @brief Destructor
+ ~CSGCGALMeshGenerator2D();
+
+ /// @brief Generate mesh
+ /// @param geometry The geometry to be meshed
+ /// @param mesh The Dolfin mesh object to be returned. Will ble cleared.
+ /// @param partition (experimental) If true then generate mesh on process 0 and partition and distribute
+ /// it. If false, then a full mesh is generated on all processes.(only
+ /// relevant when running in parallel)
+ std::shared_ptr<dolfin::Mesh> generate(std::shared_ptr<const CSGCGALDomain2D> domain,
+ const std::vector<std::pair<std::size_t, std::shared_ptr<const CSGCGALDomain2D>>>& subdomains
+ = std::vector<std::pair<std::size_t, std::shared_ptr<const CSGCGALDomain2D>>>());
+
+ /// Default parameter values
+ static dolfin::Parameters default_parameters()
+ {
+ dolfin::Parameters p("csg_cgal_meshgenerator");
+
+ // DEPRECATED! Use cell size
+ p.add("mesh_resolution", 64.0);
+ p.add("triangle_shape_bound", 0.125);
+ p.add("cell_size", 0.25);
+ p.add("lloyd_optimize", false);
+
+ // shorter edges in the domain will be collapsed before meshing
+ p.add("edge_truncate_tolerance", 1e-16);
+ p.add("partition", true);
+
+ return p;
+ }
+ };
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_CGAL_MESH_GENERATOR3D_H
+#define __MSHR_CGAL_MESH_GENERATOR3D_H
+
+#include <mshr/CSGCGALDomain3D.h>
+
+#include <dolfin/common/Variable.h>
+#include <memory>
+
+namespace dolfin{ class Mesh; }
+
+namespace mshr
+{
+
+ // Forward declaration
+ class CSGGeometry;
+
+ /// @brief Mesh generator for Constructive Solid Geometry (CSG)
+ /// utilizing CGALs 3D Mesh generation package.
+ ///
+ /// This class gives access to the backend specific meshing
+ /// criterias throught the parameter system.
+ class CSGCGALMeshGenerator3D : public dolfin::Variable
+ {
+ public :
+
+ /// @brief Create mesh generator
+ CSGCGALMeshGenerator3D();
+
+ /// @brief Destructor
+ ~CSGCGALMeshGenerator3D();
+
+ /// @brief Generate Dolfin mesh
+ /// @param domain The polyhedral domain to be meshed
+ std::shared_ptr<dolfin::Mesh> generate(std::shared_ptr<const CSGCGALDomain3D> domain) const;
+
+ /// @brief Default parameter values
+ static dolfin::Parameters default_parameters()
+ {
+ dolfin::Parameters p("csg_cgal_meshgenerator");
+ p.add("mesh_resolution", 64.0);
+ p.add("perturb_optimize", false);
+ p.add("exude_optimize", false);
+ p.add("lloyd_optimize", false);
+ p.add("odt_optimize", false);
+ p.add("edge_size", 0.025);
+ p.add("facet_angle", 25.0);
+ p.add("facet_size", 0.05);
+ p.add("facet_distance", 0.005);
+ p.add("cell_radius_edge_ratio", 3.0);
+ p.add("cell_size", 0.05);
+ p.add("detect_sharp_features", true);
+ p.add("feature_threshold", 70.);
+
+ return p;
+ }
+ private:
+ void generate(std::shared_ptr<const CSGCGALDomain3D> domain, dolfin::Mesh& mesh) const;
+ };
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+#ifndef __MSHR_GEOMETRIES_H
+#define __MSHR_GEOMETRIES_H
+
+#include "CSGGeometry.h"
+#include <dolfin/geometry/Point.h>
+
+#include <memory>
+
+namespace dolfin
+{
+ class Mesh;
+}
+
+namespace mshr
+{
+ class CSGGeometries
+ {
+ public:
+
+ // A standard LEGO brick starting at the point x with (n0, n1)
+ // knobs and height n2. The height should be 1 for a thin brick or 3
+ // for a regular brick.
+ static std::shared_ptr<CSGGeometry> lego(std::size_t n0,
+ std::size_t n1,
+ std::size_t n2,
+ dolfin::Point x = dolfin::Point(0,0,0));
+
+ // A simple propeller with parameters r - radius of center body, R - length of blades,
+ // w - width of blades and h - thicknes of blades
+ static std::shared_ptr<CSGGeometry> propeller(double r=0.125,
+ double R=0.5,
+ double w=0.3,
+ double h=0.025,
+ bool rotate_blades=true,
+ bool include_tip=false);
+
+ // Import boundary of mesh as CSG geometry
+ static std::shared_ptr<CSGGeometry> import_mesh(std::shared_ptr<dolfin::Mesh> mesh);
+ };
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg and 2013-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+
+#ifndef __MSHR_GEOMETRY_H
+#define __MSHR_GEOMETRY_H
+
+#include <memory>
+#include <cstddef>
+#include <vector>
+#include <list>
+
+#include <dolfin/common/Variable.h>
+#include <dolfin/geometry/Point.h>
+
+namespace mshr
+{
+
+ /// Geometry described by Constructive Solid Geometry (CSG)
+ class CSGGeometry : public dolfin::Variable
+ {
+ protected:
+ /// Constructor
+ CSGGeometry();
+
+ public:
+ /// Destructor
+ virtual ~CSGGeometry();
+
+ /// Return dimension of geometry
+ virtual std::size_t dim() const = 0;
+
+ /// Informal string representation
+ virtual std::string str(bool verbose) const = 0;
+
+ /// Define subdomain. This feature is 2D only.
+ /// The subdomain is itself a CSGGeometry and the corresponding
+ /// cells in the resulting will be marked with i
+ /// If subdomains overlap, the latest added will take precedence.
+ void set_subdomain(std::size_t i, std::shared_ptr<CSGGeometry> s);
+
+ /// Define subdomain. This feature is 2D only.
+ /// The subdomain is itself a CSGGeometry and the corresponding
+ /// cells in the resulting will be marked with i
+ /// If subdomains overlap, the latest added will take precedence.
+ void set_subdomain(std::size_t i, CSGGeometry& s);
+
+ /// @brief Has subdomains been set
+ bool has_subdomains() const;
+
+ /// @brief Return const list of subdomain geometries
+ const std::list<std::pair<std::size_t, std::shared_ptr<const CSGGeometry>>>& get_subdomains() const { return subdomains; }
+
+ // These functions are (for now) implemented for 2D only.
+ std::pair<dolfin::Point, double> estimate_bounding_sphere(std::size_t numSamples=100) const;
+
+ // Compute axis aligned box that is guaranteed to bound the geometry. May overshoot.
+ virtual std::pair<dolfin::Point, dolfin::Point> bounding_box() const = 0;
+
+ // Test if given point is inside geometry. 2D only (for now)
+ virtual bool inside(dolfin::Point p) const = 0;
+
+ // Test if line segment is entirely inside geometry. 2D only (for now)
+ virtual bool inside(dolfin::Point p1, dolfin::Point p2) const;
+
+ enum Type { Box,
+ Sphere,
+ Cylinder,
+ Tetrahedron,
+ Ellipsoid,
+ Surface3D,
+ Extrude2D,
+ Circle,
+ Ellipse,
+ Rectangle,
+ Polygon,
+ Union,
+ Intersection,
+ Difference,
+ Translation,
+ Scaling,
+ Rotation,
+ TriPolyhedron };
+
+ virtual Type getType() const = 0;
+ virtual bool is_operator() const = 0;
+
+ private :
+ std::list<std::pair<std::size_t, std::shared_ptr<const CSGGeometry> > > subdomains;
+ };
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg, 2012-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __MSHR_OPERATORS_H
+#define __MSHR_OPERATORS_H
+
+#include "CSGGeometry.h"
+#include <dolfin/geometry/Point.h>
+#include <dolfin/common/NoDeleter.h>
+
+#include <memory>
+
+namespace mshr
+{
+
+ /// @brief Base class for csg operators
+ /// CSGOperator object are internal (non-leaf) nodes in the CSG tree.
+ class CSGOperator : public CSGGeometry
+ {
+ protected:
+ CSGOperator();
+ public:
+ virtual bool is_operator() const { return true; }
+ std::size_t dim() const { return dim_;}
+
+ protected :
+ std::size_t dim_;
+ };
+
+ /// @brief Union of CSG geometries
+ class CSGUnion : public CSGOperator
+ {
+ public:
+
+ /// @brief Create union of two geometries
+ /// @param g0 a CSG geometry
+ /// @param g1 a CSG geometry
+ CSGUnion(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1);
+
+ /// @brief get informal string representation
+ /// @param verbose vervosity level
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ Type getType() const { return CSGGeometry::Union; }
+
+ const std::shared_ptr<CSGGeometry> _g0;
+ const std::shared_ptr<CSGGeometry> _g1;
+ };
+
+ /// @brief Difference of CSG geometries
+ class CSGDifference : public CSGOperator
+ {
+ public:
+
+ /// @brief Create union of two geometries
+ /// @param g0 a CSG geometry
+ /// @param g1 a CSG CSG geometry
+ CSGDifference(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1);
+
+ /// @brief get informal string representation
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ Type getType() const { return CSGGeometry::Difference; }
+
+ const std::shared_ptr<CSGGeometry> _g0;
+ const std::shared_ptr<CSGGeometry> _g1;
+ };
+
+
+ /// @brief Intersection of CSG geometries
+ class CSGIntersection : public CSGOperator
+ {
+ public:
+
+ /// @brief Create intersection of two geometries
+ /// @param g0 a CSG geometry
+ /// @param g1 a CSG geometry
+ CSGIntersection(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1);
+
+ /// @brief get informal string representation
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ Type getType() const { return CSGGeometry::Intersection; }
+
+ const std::shared_ptr<CSGGeometry> _g0;
+ const std::shared_ptr<CSGGeometry> _g1;
+
+ };
+
+ /// @brief Translate CSG geometry by vector
+ ///
+ /// { 'small-icon' : 'translation-small.png' }
+ class CSGTranslation : public CSGOperator
+ {
+ public:
+
+ /// @brief create translation
+ /// @param g a CSG geometry
+ /// @param t the translation vector
+ CSGTranslation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point t);
+
+ /// @brief get informal string representation
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ Type getType() const { return CSGGeometry::Translation; }
+
+ const std::shared_ptr<CSGGeometry> g;
+ const dolfin::Point t;
+ };
+
+ /// @brief Scale CSG geometry
+ ///
+ /// { 'small-icon' : 'scaling-small.png' }
+ class CSGScaling : public CSGOperator
+ {
+ public:
+
+ /// @brief Create scaling
+ /// @param g a CSG geometry
+ /// @param scale_factor
+ CSGScaling(std::shared_ptr<CSGGeometry> g,
+ double scale_factor);
+
+ /// @brief Scale (translated) geometry. Geometry will be translated, scaled and translated back
+ /// @param g a CSG geometry
+ /// @param c translation
+ /// @param scale_factor the scale factor
+ CSGScaling(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point c,
+ double scale_factor);
+
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ Type getType() const { return CSGGeometry::Scaling; }
+
+ const std::shared_ptr<CSGGeometry> g;
+ const dolfin::Point c;
+ const double s;
+ const bool translate;
+ };
+
+ /// @brief Rotate CSG geometry
+ ///
+ /// { 'small-icon' : 'rotation-small.png' }
+ class CSGRotation : public CSGOperator
+ {
+ public:
+ /// @brief Create 2D rotation (2D only)
+ /// @param g A CSG geometry
+ /// @param theta Rotate by theta
+ CSGRotation(std::shared_ptr<CSGGeometry> g,
+ double theta);
+
+ /// @brief Create rotation
+ /// @param g A CSG geometry
+ /// @param v In 2D: the rotation center. In 3D: the rotation axis.
+ /// @param theta Radians to rotate.
+ CSGRotation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point v,
+ double theta);
+
+ /// @brief create 3D rotation
+ /// @param g A CSG geometry
+ /// @param rot_axis The rotation axis
+ /// @param rot_center The rotation center
+ /// @param theta Radians to rotate
+ CSGRotation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point rot_axis,
+ dolfin::Point rot_center,
+ double theta);
+ Type getType() const { return CSGGeometry::Rotation; }
+ std::string str(bool verbose) const;
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ const std::shared_ptr<CSGGeometry> g;
+ const dolfin::Point rot_axis;
+ const dolfin::Point c;
+ const double theta;
+ const bool translate;
+ };
+
+ //--- Union operators ---
+
+ /// Create union of two geometries
+ inline std::shared_ptr<CSGUnion> operator+(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return std::shared_ptr<CSGUnion>(new CSGUnion(g0, g1));
+ }
+
+ /// Create union of two geometries
+ inline std::shared_ptr<CSGUnion> operator+(CSGGeometry& g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return reference_to_no_delete_pointer(g0) + g1;
+ }
+
+ /// Create union of two geometries
+ inline std::shared_ptr<CSGUnion> operator+(std::shared_ptr<CSGGeometry> g0,
+ CSGGeometry& g1)
+ {
+ return g0 + reference_to_no_delete_pointer(g1);
+ }
+
+ /// Create union of two geometries
+ inline std::shared_ptr<CSGUnion> operator+(CSGGeometry& g0,
+ CSGGeometry& g1)
+ {
+ return reference_to_no_delete_pointer(g0) + reference_to_no_delete_pointer(g1);
+ }
+
+ //--- Difference operators ---
+
+ /// Create difference of two geometries
+ inline std::shared_ptr<CSGDifference> operator-(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return std::shared_ptr<CSGDifference>(new CSGDifference(g0, g1));
+ }
+
+ /// Create difference of two geometries
+ inline std::shared_ptr<CSGDifference> operator-(CSGGeometry& g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return reference_to_no_delete_pointer(g0) - g1;
+ }
+
+ /// Create union of two geometries
+ inline std::shared_ptr<CSGDifference> operator-(std::shared_ptr<CSGGeometry> g0,
+ CSGGeometry& g1)
+ {
+ return g0 - reference_to_no_delete_pointer(g1);
+ }
+
+ /// Create difference of two geometries
+ inline std::shared_ptr<CSGDifference> operator-(CSGGeometry& g0,
+ CSGGeometry& g1)
+ {
+ return reference_to_no_delete_pointer(g0) - reference_to_no_delete_pointer(g1);
+ }
+
+
+ //--- Intersection operators ---
+
+ /// Create intersection of two geometries
+ inline std::shared_ptr<CSGIntersection> operator*(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return std::shared_ptr<CSGIntersection>(new CSGIntersection(g0, g1));
+ }
+
+ /// Create intersection of two geometries
+ inline std::shared_ptr<CSGIntersection> operator*(CSGGeometry& g0,
+ std::shared_ptr<CSGGeometry> g1)
+ {
+ return reference_to_no_delete_pointer(g0) * g1;
+ }
+
+ /// Create intersection of two geometries
+ inline std::shared_ptr<CSGIntersection> operator*(std::shared_ptr<CSGGeometry> g0,
+ CSGGeometry& g1)
+ {
+ return g0 * reference_to_no_delete_pointer(g1);
+ }
+
+ /// Create intersection of two geometries
+ inline std::shared_ptr<CSGIntersection> operator*(CSGGeometry& g0,
+ CSGGeometry& g1)
+ {
+ return reference_to_no_delete_pointer(g0) * reference_to_no_delete_pointer(g1);
+ }
+
+ //--- Translation operators ---
+ inline std::shared_ptr<CSGTranslation> operator+(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point t)
+ {
+ return std::shared_ptr<CSGTranslation>(new CSGTranslation(g, t));
+ }
+
+ //--- Scaling operators ---
+ inline std::shared_ptr<CSGScaling> operator*(std::shared_ptr<CSGGeometry> g,
+ double s)
+ {
+ return std::shared_ptr<CSGScaling>(new CSGScaling(g, s));
+ }
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Benjamin Kehlet, 2012-2013
+
+#ifndef __MSHR_PRIMITIVE_H
+#define __MSHR_PRIMITIVE_H
+
+#include "CSGGeometry.h"
+
+namespace mshr
+{
+
+ /// Base class for Constructive Solid Geometry (CSG) primitives.
+ class CSGPrimitive : public CSGGeometry
+ {
+ public:
+ virtual bool is_operator() const { return false; }
+ virtual std::size_t dim() const = 0;
+ };
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg, 2012-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+
+#ifndef __MSHR_PRIMITIVES_2D_H
+#define __MSHR_PRIMITIVES_2D_H
+
+#include "CSGPrimitive.h"
+
+#include <dolfin/geometry/Point.h>
+#include <vector>
+
+
+namespace mshr
+{
+
+ /// @brief Base class for 2D primitives
+ class CSGPrimitive2D : public CSGPrimitive
+ {
+ protected:
+ CSGPrimitive2D();
+
+ public:
+
+ /// @brief get the dimension of the geometry (2)
+ std::size_t dim() const { return 2; }
+ };
+
+ /// @brief A 2D circle
+ ///
+ /// { "small-icon" : "circle-small.png" }
+ class Circle : public CSGPrimitive2D
+ {
+ public:
+
+ /// @brief Create circle centered at x with radius r.
+ ///
+ /// @param c center.
+ /// @param r radius.
+ /// @param segments number of segments when computing the polygonal approximation
+ Circle(dolfin::Point c, double r, std::size_t segments=0);
+
+ /// @brief get informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Circle; }
+
+ /// @brief get center of circle
+ dolfin::Point center() const { return c; }
+
+ /// @brief get radius of circle
+ double radius() const { return _r; }
+
+ /// @brief get number of segments used when computing polygonal approximation
+ std::size_t segments() const { return _segments; }
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ private:
+ dolfin::Point c;
+ double _r;
+ const std::size_t _segments;
+ };
+
+ /// @brief A 2D ellipse
+ ///
+ /// { "small-icon" : "ellipse-small.png" }
+ class Ellipse : public CSGPrimitive2D
+ {
+ public:
+
+ /// @brief Create ellipse centered at c with horizontal semi-axis a and
+ /// vertical semi-axis b.
+ ///
+ /// @param c the center
+ /// @param a the horizontal semi-axis
+ /// @param b the vertical semi-axis
+ /// @param segments the resolution when computing polygonal approximation
+ Ellipse(dolfin::Point c, double a, double b, std::size_t segments=0);
+
+ /// @brief get informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Ellipse; }
+
+ /// @brief get center of ellipse
+ dolfin::Point center() const { return c; }
+
+ /// @brief get horizontal semi-axis
+ double a() const { return _a; }
+
+ /// @brief get vertical semi-axis
+ double b() const { return _b; }
+
+ /// @brief get resolution when computing polygonal approximation
+ std::size_t segments() const { return _segments; }
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+
+ private:
+ dolfin::Point c;
+ double _a, _b;
+ const std::size_t _segments;
+ };
+
+ /// @brief A 2D axis aligned rectangle
+ ///
+ /// { "small-icon" : "rectangle-small.png" }
+ class Rectangle : public CSGPrimitive2D
+ {
+ public:
+
+ /// @brief Create rectangle defined by two opposite corners
+ ///
+ /// @param a first corner.
+ /// @param b second corner.
+ Rectangle(dolfin::Point a, dolfin::Point b);
+
+ /// @brief get informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Rectangle; }
+
+ /// @brief get first corner
+ dolfin::Point first_corner() const { return a; }
+
+ /// @brief get second corner
+ dolfin::Point second_corner() const { return b; }
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ private:
+ const dolfin::Point a, b;
+ };
+
+ /// @brief A 2D polygon
+ ///
+ /// { 'large-icon' : 'polygon-large.png', 'small-icon' : 'polygon-small.png' }
+ class Polygon : public CSGPrimitive2D
+ {
+ public:
+
+ /// @brief Create polygon defined by the given vertices. Vertices must be in counter-clockwise order and free of self-intersections.
+ ///
+ /// @param vertices A vector of dolfin::Points. Vertices are copied into the object.
+ Polygon(const std::vector<dolfin::Point>& vertices);
+
+ /// @brief get informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Polygon; }
+
+ /// @brief check if vertices are counter clockwise oriented.
+ bool ccw() const;
+
+ /// @brief get vertices in polygon.
+ const std::vector<dolfin::Point>& vertices() const { return _vertices; }
+
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+ bool inside(dolfin::Point p) const;
+
+ private:
+ const std::vector<dolfin::Point> _vertices;
+ };
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg, 2012-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring 2012
+
+#ifndef __MSHR_PRIMITIVES_3D_H
+#define __MSHR_PRIMITIVES_3D_H
+
+#include "CSGPrimitive.h"
+#include "CSGPrimitives2D.h"
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/geometry/Point.h>
+#include <cstddef>
+
+namespace mshr
+{
+
+ /// @brief Base class for 3D primitives
+ class CSGPrimitive3D : public CSGPrimitive
+ {
+ protected:
+ CSGPrimitive3D();
+
+ public:
+ /// @return get dimension of geometry
+ std::size_t dim() const { return 3; }
+
+ // Compute axis aligned box that is guaranteed to bound the geometry. May overshoot.
+ std::pair<dolfin::Point, dolfin::Point> bounding_box() const;
+
+ // Test if given point is inside geometry. 2D only (for now)
+ bool inside(dolfin::Point p) const;
+
+
+ };
+
+ // TODO: Make sphere a special case of ellipsoid.
+ /// @brief A 3D sphere
+ ///
+ /// { "small-icon" : "sphere-small.png" }
+ class Sphere : public CSGPrimitive3D
+ {
+ public:
+
+ /// @brief Create sphere at c with radius r.
+ ///
+ /// @param center center of sphere
+ /// @param radius radius of sphere
+ /// @param segments resolution when generating a polyhedral appoximation
+ Sphere(dolfin::Point center, double radius, std::size_t segments=10);
+
+ /// @brief Informal string representation
+ /// @param verbose Verbosity level
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Sphere; }
+
+ const dolfin::Point c;
+ const double r;
+ const std::size_t _segments;
+ };
+
+ /// @brief A 3D axis aligned box
+ ///
+ /// { "small-icon" : "box-small.png" }
+ class Box : public CSGPrimitive3D
+ {
+ public:
+
+ /// @brief Create box defined by two opposite corners
+ ///
+ /// @param a The first corner
+ /// @param b The second corner
+ Box(dolfin::Point a, dolfin::Point b);
+
+ /// @brief Informal string representation
+ std::string str(bool verbose) const;
+
+ Type getType() const { return CSGGeometry::Box; }
+
+ const dolfin::Point a, b;
+ };
+
+ /// @brief A 3D cylinder
+ ///
+ /// { "small-icon" : "cylinder-small.png" }
+ class Cylinder : public CSGPrimitive3D
+ {
+ public:
+
+ /// @brief Create cylinder defined by upper and lower center
+ /// and radius respectively.
+ ///
+ /// @param top Center at top of cylinder.
+ /// @param bottom Center at bottom of cylinder.
+ /// @param top_radius Radius top of cylinder.
+ /// @param bottom_radius Radius at botoom of cylinder.
+ /// @param segments number of faces on the side when generating a polyhedral approximation.
+ Cylinder(dolfin::Point top,
+ dolfin::Point bottom,
+ double top_radius,
+ double bottom_radius,
+ std::size_t segments=32);
+
+ /// @brief Informal string representation
+ /// @param verbose Verbosity level
+ /// @return The description string
+ std::string str(bool verbose) const;
+
+ Type getType() const
+ { return CSGGeometry::Cylinder; }
+
+ const dolfin::Point _top, _bottom;
+ const double _top_radius, _bottom_radius;
+ const std::size_t _segments;
+ };
+
+ /// @brief A 3D cone.
+ /// A cone is here just a special case of a cylinder.
+ ///
+ /// { "small-icon" : "cone-small.png" }
+ class Cone : public Cylinder
+ {
+ public:
+
+ /// @brief Create cone defined by upper and lower center and bottom radius respectively.
+ /// @param top Center at top of cone.
+ /// @param bottom Center at top of cone.
+ /// @param r bottom radius of cone.
+ /// @param segments number of faces on the side when generating a polyhedral approximation.
+ Cone(dolfin::Point top, dolfin::Point bottom, double r, std::size_t segments=32)
+ : Cylinder(top, bottom, r, 0, segments) {}
+ };
+
+ /// @brief A 3D tetrahedron
+ ///
+ /// { "small-icon" : "tetrahedron-small.png" }
+ class Tetrahedron : public CSGPrimitive3D
+ {
+ public:
+ /// @brief Create tetrahedron defined by four corner points.
+ ///
+ /// @param a Point
+ /// @param b Point
+ /// @param c Point
+ /// @param d Point
+ Tetrahedron(dolfin::Point a,
+ dolfin::Point b,
+ dolfin::Point c,
+ dolfin::Point d);
+
+ /// @brief Informal string representation
+ /// @return The description string
+ std::string str(bool verbose) const;
+
+ Type getType() const
+ { return CSGGeometry::Tetrahedron; }
+
+ const dolfin::Point a, b, c, d;
+ };
+
+ /// @brief A triangular 3D surface read from file.
+ ///
+ /// { "small-icon" : "disk-small.png" }
+ class Surface3D : public CSGPrimitive3D
+ {
+ public:
+ Surface3D(std::string filename);
+
+ // Create triangulated polyhedron from surface of mesh
+ Surface3D(std::shared_ptr<const dolfin::Mesh> mesh);
+
+ // Create triangulate polyhedron on surface of subdomain of mesh
+ Surface3D(std::shared_ptr<const dolfin::Mesh> mesh, std::size_t cell_domain);
+
+ /// @brief Informal string representation
+ /// @return The description string
+ std::string str(bool verbose) const;
+
+ Type getType() const
+ { return CSGGeometry::Surface3D; }
+
+ const std::string _filename;
+ std::shared_ptr<const dolfin::Mesh> mesh;
+
+ /// @brief Tolerance when merging close vertices
+ double vertex_tolerance;
+
+ /// @brief Tolerance when removing degenerate facets
+ double degenerate_tolerance;
+
+ /// @brief Attempt to repair if surface is not topologically valid
+ bool repair;
+
+ // @brief Read only one connected_component. Only relevant if repair==true
+ bool single_connected_component;
+
+ int sharp_features_filter;
+
+ /// @brief First facet, when reading only one connect component.
+ std::size_t first_facet;
+
+ /// @brief Flip all facets
+ bool flip_facets;
+
+ /// Dump read triangles and edges to off file (usefull for debugging)
+ std::string debug_dump;
+
+ std::size_t cell_domain;
+ bool use_cell_domain;
+ };
+
+ /// @brief An axis-aligned ellipsoid
+ class Ellipsoid : public CSGPrimitive3D
+ {
+ public:
+ /// @brief Create axis aligned ellipsoid
+ ///
+ /// @param center center of ellipsoid
+ /// @param a semi-principal axis in x direction
+ /// @param b semi-principal axis in y direction
+ /// @param c semi-principal axis in z direction
+ /// @param segments resolution when generating a polyhedral appoximation
+ Ellipsoid(dolfin::Point center, double a, double b, double c, std::size_t segments=17);
+
+ /// @brief Informal string representation
+ /// @return The description string
+ std::string str(bool verbose) const;
+
+ Type getType() const
+ { return CSGGeometry::Ellipsoid; }
+
+ const dolfin::Point center;
+ const double a, b, c;
+ const std::size_t _segments;
+ };
+
+ /// @brief A 2D polygon extruded along the z axis to 3D
+ class Extrude2D : public CSGPrimitive3D
+ {
+ public :
+ Extrude2D(std::shared_ptr<CSGGeometry>, double z);
+
+ /// @brief Informal string representation
+ /// @return The description string
+ std::string str(bool verbose) const;
+
+ Type getType() const
+ { return CSGGeometry::Extrude2D; }
+
+ std::shared_ptr<CSGGeometry> geometry_2d;
+ const double z;
+ };
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2014-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __MSHR_DOLFIN_MESH_UTILS_H
+#define __MSHR_DOLFIN_MESH_UTILS_H
+
+#include <utility>
+#include <memory>
+
+namespace dolfin { class Mesh; }
+
+namespace mshr
+{
+
+class DolfinMeshUtils
+{
+ public:
+ /// Compute the smallest and largest cell wrt. volume.
+ /// @param m The mesh
+ static std::pair<double, double> cell_volume_min_max(const dolfin::Mesh& m);
+
+ /// Check that all vertices has at least one incident cell
+ /// @param m The mesh
+ static bool has_isolated_vertices(const dolfin::Mesh& m);
+
+ /// Run all implemented checks mesh consistency
+ /// @param m The mesh
+ static bool check_mesh(const dolfin::Mesh& m);
+
+ static std::shared_ptr<dolfin::Mesh>
+ extract_subdomain(std::shared_ptr<const dolfin::Mesh>,
+ std::size_t cell_domain);
+
+ /// @brief Glue together two meshes by matching boundary vertices.
+ /// @param m1 Mesh 1 to merge
+ /// @param m2 Mesh 2 to merge
+ /// @param m1_boundary_marker Mark boundary facets the belongs to m1. A value < 0 disables marking.
+ /// @param m1_boundary_marker Mark boundary facets the belongs to m2. A value < 0 disables marking.
+ /// @param m1_boundary_marker Mark boundary facets the belongs to the interface between m1 an m2. A value < 0 disables marking.
+ static std::shared_ptr<dolfin::Mesh>
+ merge_meshes(std::shared_ptr<dolfin::Mesh> m1,
+ std::shared_ptr<dolfin::Mesh> m2,
+ int m1_marker=1,
+ int m2_marker=2,
+ int m1_boundary_marker=1,
+ int m2_boundary_marker=2,
+ int interface_marker=3);
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_GLOBALINITIALIZER_H
+#define __MSHR_GLOBALINITIALIZER_H
+
+class GlobalInitializer
+{
+ public:
+ GlobalInitializer();
+ ~GlobalInitializer();
+
+ /// The singleton instance of the GlobalInitializer.
+ static GlobalInitializer& instance();
+};
+
+#endif
--- /dev/null
+// Copyright (C) 2012 Anders Logg and 2012-2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#ifndef __MSHR_MESH_GENERATOR_H
+#define __MSHR_MESH_GENERATOR_H
+
+#include "CSGGeometry.h"
+
+namespace dolfin
+{
+ // Forward declarations
+ class Mesh;
+}
+
+namespace mshr
+{
+
+ /// Generate mesh from CSG geometry
+ std::shared_ptr<dolfin::Mesh>
+ generate_mesh(std::shared_ptr<const CSGGeometry> geometry,
+ double resolution,
+ std::string backend="cgal");
+}
+
+#endif
--- /dev/null
+// Copyright (C) -2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#include <dolfin/mesh/Mesh.h>
+
+namespace mshr
+{
+ class UnitSphereMesh : public dolfin::Mesh
+ {
+ public:
+ UnitSphereMesh(std::size_t resolution);
+ };
+}
--- /dev/null
+// Copyright (C) 2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __OFF_FILE_READER_H
+#define __OFF_FILE_READER_H
+
+#include <string>
+#include <vector>
+#include <array>
+
+namespace mshr
+{
+
+class OFFFileReader
+{
+ public:
+ /// @brief Read and parse a triangular 3d surface off file
+ /// @param filename The file to read
+ /// @param vertices A vector of points to be read into
+ /// @param facets A vector of facets given as indices to the vertex array.
+ static void read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets);
+
+ static void write(const std::string filename,
+ const std::vector<std::array<double, 3> >& vertices,
+ const std::vector<std::array<std::size_t, 3> >& facets);
+
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet & 2016 Lars Magnus Valnes
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __STL_FILE_READER_H
+#define __STL_FILE_READER_H
+
+#include <string>
+#include <vector>
+#include <array>
+
+#include <boost/tuple/tuple.hpp>
+
+namespace mshr
+{
+
+class STLFileReader
+{
+public:
+
+ /// @brief Parse STL file.
+ /// @param filename The file to be read
+ /// @param vertices array to return vertices in
+ /// @param facets array to return facets in as indices to the vertex array
+ static void read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets);
+ static void write(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets);
+};
+
+}
+#endif
--- /dev/null
+ // Copyright (C) 2014 Benjamin Kehlet
+ //
+ // This file is part of mshr.
+ //
+ // mshr 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 3 of the License, or
+ // (at your option) any later version.
+ //
+ // mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+ // OBS! Experimental
+
+ #ifndef _SURFACE_CONSISTENCY_H
+ #define _SURFACE_CONSISTENCY_H
+
+ #include <vector>
+ #include <set>
+ #include <array>
+ #include <memory>
+
+ namespace mshr
+ {
+
+ class SurfaceConsistency
+ {
+ public:
+
+ /// Check that the connectivity of the facet is consistent, ie. all edges are
+ /// shared by exactly two facets.
+ /// If error is set to True, then an error will be thrown when a duplicated
+ /// halfedges is encountered. Otherwise, the index of one of the facets will
+ /// be stored in the set.
+ static void checkConnectivity(std::vector<std::array<std::size_t, 3> >& facets,
+ std::set<std::size_t>& duplicating,
+ bool error);
+
+ static void filterFacets(const std::vector<std::array<std::size_t, 3> >& facets,
+ const std::vector<std::array<double, 3> >& vertices,
+ std::size_t start, std::set<std::size_t>& skip);
+
+ static std::pair<std::unique_ptr<std::vector<std::array<double, 3> > >,
+ std::unique_ptr<std::vector<std::array<std::size_t, 3> > > >
+ merge_close_vertices(const std::vector<std::array<std::size_t, 3> >& facets,
+ const std::vector<std::array<double, 3> >& vertices,
+ double tolerance);
+
+ static std::size_t remove_null_facets(std::vector<std::array<std::size_t, 3>>& facets);
+
+ static std::size_t remove_isolated_vertices(std::vector<std::array<double, 3>>& vertices,
+ std::vector<std::array<std::size_t, 3>>& facets);
+
+ static void orient_component(std::vector<std::array<std::size_t, 3> >& facets,
+ std::size_t start);
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __MSHR_SURFACERECONSTRUCTION_H
+#define __MSHR_SURFACERECONSTRUCTION_H
+
+#include <vector>
+#include <array>
+
+namespace mshr
+{
+ class SurfaceReconstruction
+ {
+ public:
+ static void reconstruct(const std::vector<double>& vertices,
+ const std::vector<std::size_t>& facets,
+ std::vector<std::array<double, 3>>& reconstructed_vertices,
+ std::vector<std::array<std::size_t, 3>>& reconstruct_facets,
+ double expansion);
+
+ static void remesh(double edge_length,
+ double sharp_edge_tolerance,
+ const std::vector<double>& vertices,
+ const std::vector<std::size_t>& facets,
+ std::vector<std::array<double, 3>>& remeshed_vertices,
+ std::vector<std::array<std::size_t, 3>>& remeshed_facets);
+ };
+
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2012-2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_TETGEN_MESH_GENERATOR3D_H
+#define __MSHR_TETGEN_MESH_GENERATOR3D_H
+
+#include <mshr/CSGCGALDomain3D.h>
+
+#include <dolfin/common/Variable.h>
+#include <memory>
+
+namespace dolfin{ class Mesh; }
+
+namespace mshr
+{
+
+ // Forward declaration
+ class CSGGeometry;
+
+ class TetgenMeshGenerator3D : public dolfin::Variable
+ {
+ public :
+ TetgenMeshGenerator3D();
+ ~TetgenMeshGenerator3D();
+
+ std::shared_ptr<dolfin::Mesh> generate(std::shared_ptr<const CSGCGALDomain3D> domain) const;
+
+ /// Default parameter values
+ static dolfin::Parameters default_parameters()
+ {
+ dolfin::Parameters p("tetgen_meshgenerator");
+ p.add("mesh_resolution", 64.0);
+
+ p.add("disable_quality_improvement", false);
+ p.add("preserve_surface", false);
+ p.add("max_radius_edge_ratio", 2.);
+ p.add("min_dihedral_angle", 12.);
+
+ // If set to a positive value, this will override "mesh_resolution"
+ p.add("max_tet_volume", -1.0);
+
+ return p;
+ }
+ };
+}
+
+#endif
--- /dev/null
+# - Config file for the mshr package
+# It defines the following variables
+# MSHR_INCLUDE_DIRS - include directories for mshr
+# MSHR_LIBRARIES_DIR - directory where the mshr library is located
+# MSHR_LIBRARIES - libraries to link against
+
+# Compute paths
+get_filename_component(MSHR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+set(MSHR_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@")
+set(MSHR_EXTERNAL_INCLUDE_DIRS "@CONF_EXTERNAL_INCLUDE_DIRS@")
+set(MSHR_LIBRARIES_DIRS "@CONF_LIBRARIES_DIRS@")
+set(MSHR_EXTERNAL_LIBRARIES "@CONF_EXTERNAL_LIBRARIES@")
+set(MSHR_CXX_DEFINITIONS "@CONF_CXX_DEFINITIONS@")
+set(MSHR_CXX_FLAGS "@CONF_CXX_FLAGS@")
+set(MSHR_LIBRARIES mshr)
+
+set(MSHR_USE_FILE "${MSHR_CMAKE_DIR}/use-mshr.cmake")
\ No newline at end of file
--- /dev/null
+# - Config file for the mshr package
+# It defines the following variables
+# MSHR_INCLUDE_DIRS - include directories for mshr
+# MSHR_LIBRARIES_DIR - directory where the mshr library is located
+# MSHR_LIBRARIES - libraries to link against
+
+# Compute paths
+get_filename_component(MSHR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+set(mshr_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@")
+set(mshr_EXTERNAL_INCLUDE_DIRS "@CONF_EXTERNAL_INCLUDE_DIRS@")
+set(mshr_LIBRARIES_DIRS "@CONF_LIBRARIES_DIRS@")
+set(mshr_EXTERNAL_LIBRARIES "@CONF_EXTERNAL_LIBRARIES@")
+set(mshr_CXX_DEFINITIONS "@CONF_CXX_DEFINITIONS@")
+set(mshr_CXX_FLAGS "@CONF_CXX_FLAGS@")
+set(mshr_LIBRARIES mshr)
--- /dev/null
+cmake_minimum_required(VERSION 3.5.0)
+
+
+# This script is used solely for getting the paths to the dependencies
+# of the bindings (ie. pybind11, dolfin and mshr).
+# This is written to a json file which is read from setup.py.
+
+PROJECT(mshr_pybind11_config)
+
+
+# pybind11_FOUND - true if pybind11 and all required components found on the system
+# pybind11_VERSION - pybind11 version in format Major.Minor.Release
+# pybind11_INCLUDE_DIRS - Directories where pybind11 and python headers are located.
+# pybind11_INCLUDE_DIR - Directory where pybind11 headers are located.
+# pybind11_DEFINITIONS - Definitions necessary to use pybind11, namely USING_pybind11.
+# pybind11_LIBRARIES - compile flags and python libraries (as needed) to link against.
+# pybind11_LIBRARY - empty.
+# CMAKE_MODULE_PATH - appends location of accompanying FindPythonLibsNew.cmake and
+# pybind11Tools.cmake modules.
+find_package(pybind11 CONFIG HINTS ${PYBIND11_DIR} ${PYBIND11_ROOT}
+ $ENV{PYBIND11_DIR} $ENV{PYBIND11_ROOT})
+
+
+find_package(DOLFIN)
+
+find_package(mshr)
+
+configure_file("config.json.in" "config.json")
+
+
--- /dev/null
+{
+ "pybind11" : { "found" : ${pybind11_FOUND},
+ "version" : "${pybind11_VERSION}",
+ "include_dir" : "${pybind11_INCLUDE_DIRS}",
+ "include_dirs" : "${pybind11_INCLUDE_DIR}",
+ "definitions" : "${pybind11_DEFINITIONS}"
+ },
+ "dolfin" : { "found" : ${DOLFIN_FOUND},
+ "include_dirs" : "${DOLFIN_INCLUDE_DIRS}"
+ },
+ "mshr" : { "found" : ${mshr_FOUND},
+ "include_dirs" : "${mshr_INCLUDE_DIRS}",
+ "lib_dirs" : "${mshr_LIBRARIES_DIRS}",
+ "libs" : "${mshr_LIBRARIES}"
+ }
+}
--- /dev/null
+# -*- coding: utf-8 -*-
+"""mshr package"""
+
+# Copyright (C) 2017 Benjamin Kehlet
+
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http:#www.gnu.org/licenses/>.
+#
+
+import dolfin
+
+from .cpp import Circle
+from .cpp import Ellipse
+from .cpp import Rectangle
+from .cpp import Polygon
+from .cpp import Sphere
+from .cpp import Box
+from .cpp import Cylinder
+from .cpp import Cone
+from .cpp import Tetrahedron
+from .cpp import Surface3D
+from .cpp import Ellipsoid
+from .cpp import Rectangle
+from .cpp import Extrude2D
+from .cpp import CSGCGALDomain3D
+
+from .cpp import CSGIntersection
+from .cpp import CSGDifference
+from .cpp import CSGUnion
+from .cpp import CSGScaling
+from .cpp import CSGTranslation
+from .cpp import CSGRotation
+
+from .cpp import CSGGeometries
+from .cpp import UnitSphereMesh
+
+from .cpp import CSGCGALMeshGenerator3D
+from .cpp import TetgenMeshGenerator3D
+
+from .cpp import CSGCGALDomain2D
+from .cpp import CSGCGALMeshGenerator2D
+
+
+from .cpp import generate_mesh
--- /dev/null
+import os, sys
+import subprocess
+import json
+
+from setuptools import setup, Extension
+
+
+# Call cmake to generate json file with include and link information about dolfin and pybind11
+if not os.path.isfile(os.path.join("build", "config.json")) :
+ if not os.path.exists("build") :
+ os.mkdir("build")
+ subprocess.check_call(["cmake", os.getcwd()], cwd=os.path.abspath("build"))
+
+with open(os.path.join("build", "config.json"), 'r') as infile :
+ config = json.load(infile)
+
+include_dirs = config["pybind11"]["include_dirs"].split(";") + \
+ config["dolfin"]["include_dirs"].split(";") + \
+ config["mshr"]["include_dirs"].split(";")
+
+
+mshr_ext = Extension('mshr.cpp',
+ ['src/mshr.cpp'],
+ include_dirs=include_dirs,
+ library_dirs=config['mshr']['lib_dirs'].split(";"),
+ libraries=config['mshr']['libs'].split(";"),
+ extra_compile_args=['-std=c++11'],
+ language='c++11')
+
+
+setup(name = 'mshr',
+ version = '2018.1.0',
+ author = 'FEniCS Project',
+ description = 'mshr python interface (via pybind11)',
+ long_description = '',
+ packages = ["mshr",],
+ ext_modules = [mshr_ext],
+ install_requires = ["numpy", "fenics-dolfin"]
+ #zip_safe = False)
+ )
--- /dev/null
+#include <pybind11/pybind11.h>
+#include <pybind11/operators.h>
+#include <pybind11/stl.h>
+
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGPrimitives2D.h>
+#include <mshr/CSGPrimitives3D.h>
+#include <mshr/CSGCGALDomain2D.h>
+#include <mshr/CSGCGALDomain3D.h>
+#include <mshr/CSGOperators.h>
+#include <mshr/MeshGenerator.h>
+#include <mshr/CSGGeometries3D.h>
+#include <mshr/CSGCGALMeshGenerator3D.h>
+#include <mshr/CSGCGALMeshGenerator2D.h>
+#include <mshr/TetgenMeshGenerator3D.h>
+#include <mshr/Meshes.h>
+
+namespace py = pybind11;
+
+
+PYBIND11_MODULE(cpp, m)
+{
+ // Create module for C++ wrappers
+ m.doc() ="mshr python interface";
+
+ // CSGGeometry
+ py::class_<mshr::CSGGeometry, dolfin::Variable,
+ std::shared_ptr<mshr::CSGGeometry>> (m, "CSGGeometry")
+ .def("dim", &mshr::CSGGeometry::dim)
+ .def("set_subdomain",
+ static_cast<void(mshr::CSGGeometry::*)(std::size_t i,
+ std::shared_ptr<mshr::CSGGeometry> s)>(&mshr::CSGGeometry::set_subdomain))
+ .def("has_subdomains", &mshr::CSGGeometry::has_subdomains)
+ .def("inside", static_cast<bool (mshr::CSGGeometry::*)(dolfin::Point) const>(&mshr::CSGGeometry::inside))
+ .def("__mul__",
+ static_cast<std::shared_ptr<mshr::CSGIntersection>(*)(std::shared_ptr<mshr::CSGGeometry>,
+ std::shared_ptr<mshr::CSGGeometry>)>(&mshr::operator*),
+ py::is_operator())
+
+ .def("__mul__",
+ static_cast<std::shared_ptr<mshr::CSGScaling>(*)(std::shared_ptr<mshr::CSGGeometry>,
+ double)>(&mshr::operator*),
+ py::is_operator())
+
+ .def("__add__",
+ static_cast<std::shared_ptr<mshr::CSGUnion>(*)(std::shared_ptr<mshr::CSGGeometry>,
+ std::shared_ptr<mshr::CSGGeometry>)>(&mshr::operator+),
+ py::is_operator())
+
+ .def("__sub__",
+ static_cast<std::shared_ptr<mshr::CSGDifference>(*)(std::shared_ptr<mshr::CSGGeometry>,
+ std::shared_ptr<mshr::CSGGeometry>)>(&mshr::operator-),
+ py::is_operator());
+
+
+ py::class_<mshr::CSGUnion, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGUnion>>(m, "CSGUnion");
+
+ py::class_<mshr::CSGIntersection, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGIntersection>>(m, "CSGIntersection");
+
+ py::class_<mshr::CSGDifference, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGDifference>>(m, "CSGDifference");
+
+ py::class_<mshr::CSGScaling, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGScaling>>(m, "CSGScaling")
+ .def(py::init<std::shared_ptr<mshr::CSGGeometry>, double>());
+
+ py::class_<mshr::CSGTranslation, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGTranslation>>(m, "CSGTranslation")
+ .def(py::init<std::shared_ptr<mshr::CSGGeometry>, dolfin::Point>());
+
+ py::class_<mshr::CSGRotation, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGRotation>>(m, "CSGRotation")
+ .def(py::init<std::shared_ptr<mshr::CSGGeometry>, dolfin::Point, double>())
+ .def(py::init<std::shared_ptr<mshr::CSGGeometry>, double>());
+
+
+
+ // Circle
+ py::class_<mshr::Circle, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Circle>>(m, "Circle")
+ .def(py::init<dolfin::Point, double, std::size_t>(),
+ py::arg("c"), py::arg("r"), py::arg("segments")=0)
+ .def("center", &mshr::Circle::center)
+ .def("radius", &mshr::Circle::radius);
+
+ // Ellipse
+ py::class_<mshr::Ellipse, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Ellipse>>(m, "Ellipse")
+ .def(py::init<dolfin::Point, double, double, std::size_t>(),
+ py::arg("c"), py::arg("a"), py::arg("b"), py::arg("segments")=0)
+ .def("center", &mshr::Ellipse::center)
+ .def("a", &mshr::Ellipse::a)
+ .def("b", &mshr::Ellipse::b);
+
+ // Rectangle
+ py::class_<mshr::Rectangle, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Rectangle>> (m, "Rectangle")
+ .def(py::init<dolfin::Point, dolfin::Point>())
+ .def("first_corner", &mshr::Rectangle::first_corner)
+ .def("second_corne", &mshr::Rectangle::second_corner);
+
+ // Polygon
+ py::class_<mshr::Polygon, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Polygon>>(m, "Polygon")
+ .def(py::init<std::vector<dolfin::Point>&>())
+ .def("ccw", &mshr::Polygon::ccw)
+ .def("vertices", &mshr::Polygon::vertices);
+
+ // Sphere
+ py::class_<mshr::Sphere, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Sphere>>(m, "Sphere")
+ .def(py::init<dolfin::Point, double, std::size_t>(),
+ py::arg("center"), py::arg("radius"), py::arg("segments")=10);
+
+ // Box
+ py::class_<mshr::Box, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Box>>(m, "Box")
+ .def(py::init<dolfin::Point, dolfin::Point>());
+
+ // Cylinder
+ py::class_<mshr::Cylinder, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Cylinder>>(m, "Cylinder")
+ .def(py::init<dolfin::Point, dolfin::Point, double, double, std::size_t>(),
+ py::arg("top"),
+ py::arg("bottom"),
+ py::arg("top_radius"),
+ py::arg("bottom_radius"),
+ py::arg("segments")=32);
+
+ // Cone
+ py::class_<mshr::Cone, mshr::Cylinder,
+ std::shared_ptr<mshr::Cone>>(m, "Cone")
+ .def(py::init<dolfin::Point, dolfin::Point, double, std::size_t>(),
+ py::arg("top"), py::arg("bottom"), py::arg("r"), py::arg("segments")=32);
+
+ // Tetrahedron
+ py::class_<mshr::Tetrahedron, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Tetrahedron>>(m, "Tetrahedron")
+ .def(py::init<dolfin::Point, dolfin::Point, dolfin::Point, dolfin::Point>());
+
+ // Surface3D (deprecated, use CSGCGALDomain3D)
+ py::class_<mshr::Surface3D, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Surface3D>>(m, "Surface3D")
+ .def(py::init<std::string>());
+
+ // Ellipsoid
+ py::class_<mshr::Ellipsoid, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Ellipsoid>>(m, "Ellipsoid")
+ .def(py::init<dolfin::Point, double, double, double, std::size_t>(),
+ py::arg("center"),
+ py::arg("a"), py::arg("b"), py::arg("c"), py::arg("segments")=15);
+
+ // Extrude2D
+ py::class_<mshr::Extrude2D, mshr::CSGGeometry,
+ std::shared_ptr<mshr::Extrude2D>>(m, "Extrude2D")
+ .def(py::init<std::shared_ptr<mshr::CSGGeometry>, double>());
+
+ // CSGCGALDomain3D
+ py::class_<mshr::CSGCGALDomain3D, mshr::CSGGeometry,
+ std::shared_ptr<mshr::CSGCGALDomain3D>>(m, "CSGCGALDomain3D")
+ .def(py::init<std::shared_ptr<const mshr::CSGGeometry>>())
+ .def("num_vertices", &mshr::CSGCGALDomain3D::num_vertices)
+ .def("num_facets", &mshr::CSGCGALDomain3D::num_facets)
+ .def("num_halfedges", &mshr::CSGCGALDomain3D::num_halfedges)
+ .def("num_degenerate_facets", &mshr::CSGCGALDomain3D::num_degenerate_facets)
+ .def("facet_area_minmax", &mshr::CSGCGALDomain3D::facet_area_minmax)
+ .def("edge_length_minmax", &mshr::CSGCGALDomain3D::edge_length_range)
+ .def("num_short_edges", &mshr::CSGCGALDomain3D::num_short_edges)
+ .def("volume", &mshr::CSGCGALDomain3D::volume)
+ .def("ensure_meshing_preconditions", &mshr::CSGCGALDomain3D::ensure_meshing_preconditions)
+ .def("is_selfintersecting", &mshr::CSGCGALDomain3D::is_selfintersecting)
+ .def("remove_selfintersectione", &mshr::CSGCGALDomain3D::remove_selfintersections)
+ .def("save", &mshr::CSGCGALDomain3D::save)
+ .def("num_disconnected_components", &mshr::CSGCGALDomain3D::num_disconnected_components)
+ .def("remesh_surface", &mshr::CSGCGALDomain3D::remesh_surface)
+ .def("remove_degenerate_facets", &mshr::CSGCGALDomain3D::remove_degenerate_facets)
+ .def("convex_hull", static_cast<std::shared_ptr<mshr::CSGCGALDomain3D>(mshr::CSGCGALDomain3D::*)() const>(&mshr::CSGCGALDomain3D::convex_hull))
+ ;
+
+ py::class_<mshr::CSGGeometries>(m, "CSGGeometries")
+ .def_static("lego", &mshr::CSGGeometries::lego)
+ .def_static("propeller", &mshr::CSGGeometries::propeller);
+
+ py::class_<mshr::UnitSphereMesh>(m, "UnitSphereMesh")
+ .def(py::init<std::size_t>());
+
+ py::class_<mshr::CSGCGALMeshGenerator3D, dolfin::Variable,
+ std::shared_ptr<mshr::CSGCGALMeshGenerator3D>>(m, "CSGCGALMeshGenerator3D")
+ .def(py::init<>())
+ .def("generate", static_cast<std::shared_ptr<dolfin::Mesh>(mshr::CSGCGALMeshGenerator3D::*)(std::shared_ptr<const mshr::CSGCGALDomain3D>) const>(&mshr::CSGCGALMeshGenerator3D::generate));
+
+ py::class_<mshr::CSGCGALDomain2D, dolfin::Variable,
+ std::shared_ptr<mshr::CSGCGALDomain2D>>(m, "CSGCGALDomain2D")
+ .def(py::init<std::shared_ptr<const mshr::CSGGeometry>, double>());
+
+ py::class_<mshr::CSGCGALMeshGenerator2D, dolfin::Variable,
+ std::shared_ptr<mshr::CSGCGALMeshGenerator2D>>(m, "CSGCGALMeshGenerator2D")
+ .def(py::init<>())
+ .def("generate", &mshr::CSGCGALMeshGenerator2D::generate);
+
+
+ py::class_<mshr::TetgenMeshGenerator3D, dolfin::Variable,
+ std::shared_ptr<mshr::TetgenMeshGenerator3D>>(m, "TetgenMeshGenerator3D")
+ .def(py::init<>())
+ .def("generate", &mshr::TetgenMeshGenerator3D::generate);
+
+ // generate_mesh
+ m.def("generate_mesh",
+ &mshr::generate_mesh,
+ py::arg("geoemtry"), py::arg("resolution"), py::arg("backend")="cgal");
+}
--- /dev/null
+# Configuration file for fenics-release
+
+PACKAGE="mshr"
+BRANCH="master"
+FILES="ChangeLog.rst CMakeLists.txt"
--- /dev/null
+// Copyright (C) 2016 Lars Magnus Valnes
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/ASCFileReader.h>
+
+#include <dolfin/geometry/Point.h>
+#include <dolfin/common/constants.h>
+#include <dolfin/log/LogStream.h>
+#include <dolfin/log/log.h>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+#include <boost/filesystem.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <algorithm>
+#include <string>
+#include <map>
+
+namespace
+{
+template<typename T>
+inline double convert_string(const std::string& s)
+{
+ std::istringstream is(s);
+ T val;
+ is >> val;
+
+ return val;
+}
+
+// get next line of file and trim away whitespace
+inline void get_next_line(std::ifstream& file, std::string& line, std::size_t &lineno)
+{
+ do
+ {
+ std::getline(file, line);
+ boost::algorithm::trim(line);
+ lineno++;
+ } while ( !file.eof() && line == "");
+}
+} // end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+
+void ASCFileReader::read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets)
+{
+ dolfin::log(dolfin:: TRACE, "Reading surface from %s ", filename.c_str());
+
+ vertices.clear();
+ facets.clear();
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+
+ std::ifstream file(filename.c_str());
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("ASCFileReader.cpp",
+ "open .asc file to read 3D surface",
+ "Failed to open file");
+ }
+
+ std::string line;
+ std::size_t lineno = 0;
+ const boost::char_separator<char> sep(" ");
+ const boost::char_separator<char> sep2(" ");
+
+ get_next_line(file, line, lineno);
+ // First line is not important
+ get_next_line(file, line, lineno);
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+ const std::size_t num_vertices = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ const std::size_t num_facets = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ dolfin_assert(tok_iter == tokens.end());
+
+ vertices.reserve(num_vertices);
+ facets.reserve(num_facets);
+
+ get_next_line(file, line, lineno);
+ for (std::size_t i = 0; i < num_vertices; i++)
+ {
+ tokenizer tokens(line, sep2);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ std::array<double, 3> vertex;
+ vertex[0] = convert_string<double>(*tok_iter);
+ tok_iter++;
+ vertex[1] = convert_string<double>(*tok_iter);
+ tok_iter++;
+ vertex[2] = convert_string<double>(*tok_iter);
+ tok_iter++;
+
+ tok_iter++;
+
+ dolfin_assert(tok_iter == tokens.end());
+ vertices.push_back(vertex);
+ get_next_line(file, line, lineno);
+ }
+ for (std::size_t i = 0; i < num_facets; i++)
+ {
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ std::array<std::size_t, 3> facet;
+ facet[0] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ facet[1] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ facet[2] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ tok_iter++;
+
+ dolfin_assert(tok_iter == tokens.end());
+
+ facets.push_back(facet);
+ get_next_line(file, line, lineno);
+
+ }
+
+}
+//-----------------------------------------------------------------------------
+void ASCFileReader::write(const std::string filename,
+ const std::vector<std::array<double, 3> >& vertices,
+ const std::vector<std::array<std::size_t, 3> >& facets)
+{
+ std::ofstream file(filename);
+ file.precision(6);
+
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("ASCFileReader.cpp",
+ "open file to write asc data",
+ "Failed to open file");
+ }
+
+ file << "#!" << std::endl;
+ file << vertices.size() << " " << facets.size() << std::endl;
+ // Orded vertices
+ for (const std::array<double, 3>& v : vertices)
+ {
+ file << v[0] << " " << v[1] << " " << v[2] << " " << 0 << std::endl;
+ }
+
+ for (const std::array<std::size_t, 3>& f : facets)
+ {
+ file << f[0] << " " << f[1] << " " << f[2] << " " << 0 << std::endl;
+ }
+}
+}
--- /dev/null
+// Copyright (C) 2013-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+// This file must be included to get the compiler flags
+// They should ideally have been added via the command line,
+// but since CGAL configure time is at mshr compile time, we don't
+// have access to CGALConfig.cmake at configure time...
+#ifndef CGAL_HEADER_ONLY
+#include <CGAL/compiler_config.h>
+#endif
+#include <CGAL/Cartesian.h>
+#include <CGAL/Quotient.h>
+#include <CGAL/MP_Float.h>
+
+#include <mshr/CSGCGALDomain2D.h>
+#include <mshr/CSGPrimitives2D.h>
+#include <mshr/CSGOperators.h>
+
+#include <dolfin/common/constants.h>
+#include <dolfin/log/LogStream.h>
+
+#include <CGAL/basic.h>
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Boolean_set_operations_2.h>
+#include <CGAL/Polygon_set_2.h>
+
+#include <CGAL/Min_circle_2.h>
+#include <CGAL/Min_circle_2_traits_2.h>
+
+// Polygon typedefs
+//typedef CGAL::Exact_predicates_exact_constructions_kernel Exact_Kernel;
+typedef CGAL::Quotient<CGAL::MP_Float> FT;
+typedef CGAL::Cartesian<FT> Exact_Kernel;
+
+typedef Exact_Kernel::Point_2 Point_2;
+typedef Exact_Kernel::Vector_2 Vector_2;
+typedef Exact_Kernel::Segment_2 Segment_2;
+typedef Exact_Kernel::Ray_2 Ray_2;
+typedef Exact_Kernel::Direction_2 Direction_2;
+typedef Exact_Kernel::Aff_transformation_2 Aff_transformation_2;
+typedef CGAL::Polygon_2<Exact_Kernel> Polygon_2;
+typedef Polygon_2::Vertex_const_iterator Vertex_const_iterator;
+typedef CGAL::Polygon_with_holes_2<Exact_Kernel> Polygon_with_holes_2;
+typedef Polygon_with_holes_2::Hole_const_iterator Hole_const_iterator;
+typedef CGAL::Polygon_set_2<Exact_Kernel> Polygon_set_2;
+
+// Min enclosing circle typedefs
+typedef CGAL::Min_circle_2_traits_2<Exact_Kernel> Min_Circle_Traits;
+typedef CGAL::Min_circle_2<Min_Circle_Traits> Min_circle;
+typedef CGAL::Circle_2<Exact_Kernel> CGAL_Circle;
+
+
+namespace
+{
+FT get_shortest_edge(const Polygon_set_2& polygon_set)
+{
+ FT shortest_edge = std::numeric_limits<double>::max();
+
+ std::list<Polygon_with_holes_2> polygon_list;
+ polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ // FIXME: For now it looks only at the outer boundary.
+ // Should take holes into account as well
+ for (const Polygon_with_holes_2& p : polygon_list)
+ {
+ const Polygon_2& bdr = p.outer_boundary();
+ Point_2 prev = bdr.container().back();
+ for (Polygon_2::Vertex_const_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ shortest_edge = std::min(shortest_edge, (*vit-prev).squared_length());
+ prev = *vit;
+ }
+ }
+ return shortest_edge;
+}
+//-----------------------------------------------------------------------------
+Point_2 point_in_polygon(const Polygon_2& p)
+{
+ // Take the midpoint, s, of a segment, then do a ray shooting in the inwards
+ // normal direction and return the midpoint between s and the closest ray hit.
+
+ Polygon_2::Edge_const_iterator eit = p.edges_begin();
+ const Point_2 source = CGAL::midpoint(eit->source(), eit->target());
+ const Aff_transformation_2 tr = Aff_transformation_2(CGAL::ROTATION,
+ p.orientation() == CGAL::COUNTERCLOCKWISE ? 1 : -1, 0);
+
+ const Ray_2 r(source, tr(Direction_2(*eit)));
+ Point_2 closest;
+ FT min_squared_distance = std::numeric_limits<double>::max();
+
+ eit++;
+ for (; eit != p.edges_end(); eit++)
+ {
+ auto ii = CGAL::intersection(r, *eit);
+ if (ii)
+ {
+ if (const Point_2* p = boost::get<Point_2>(&*ii))
+ {
+ const FT squared_distance = CGAL::squared_distance(source, *p);
+ if (squared_distance < min_squared_distance)
+ {
+ closest = *p;
+ min_squared_distance = squared_distance;
+ }
+ }
+ else if (const Segment_2* s = boost::get<Segment_2>(&*ii))
+ {
+ // If the intersection is a segment, an edge in the polygon is paralell
+ // to the ray. Simply check both the source and the target point of the
+ // segment.
+
+ {
+ const FT squared_distance = CGAL::squared_distance(source, s->source());
+ if (squared_distance < min_squared_distance)
+ {
+ closest = *p;
+ min_squared_distance = squared_distance;
+ }
+ }
+
+ {
+ const FT squared_distance = CGAL::squared_distance(source, s->target());
+ if (squared_distance < min_squared_distance)
+ {
+ closest = *p;
+ min_squared_distance = squared_distance;
+ }
+ }
+ } // end intersection is Segment_2
+ }
+ }
+ return CGAL::midpoint(source, closest);
+}
+//-----------------------------------------------------------------------------
+void truncate_short_edges(Polygon_set_2& polygon_set,
+ FT tolerance,
+ const std::set<Point_2> & collapsable_vertices = std::set<Point_2>())
+{
+ const FT tolerance_squared = tolerance*tolerance;
+
+ Polygon_set_2 truncated_set;
+ std::list<Polygon_with_holes_2> polygon_list;
+ polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ // FIXME: For now it looks only at the outer boundary.
+ // Should take holes into account as well
+ for (const Polygon_with_holes_2& p : polygon_list)
+ {
+ Polygon_2 bdr = p.outer_boundary();
+
+ bool changed = true;
+ while(changed)
+ {
+ changed = false;
+
+ Polygon_2::Vertex_iterator prev = bdr.vertices_end()-1;
+
+ for (Polygon_2::Vertex_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ if ( (*vit-*prev).squared_length() < tolerance_squared)
+ {
+ if (collapsable_vertices.size() == 0 || collapsable_vertices.find(*vit) != collapsable_vertices.end())
+ {
+ bdr.erase(vit);
+ changed = true;
+ break;
+ }
+ else if (collapsable_vertices.size() == 0 || collapsable_vertices.find(*prev) != collapsable_vertices.end())
+ {
+ bdr.erase(prev);
+ changed = true;
+ break;
+ }
+ else
+ {
+ // std::cout << "Couldn't erase vertex" << std::endl;
+ }
+ } // end if short edge
+ prev = vit;
+ } // end vertex iteration
+ } // end while(changed)
+
+ Polygon_with_holes_2 pwh(bdr);
+ for (Hole_const_iterator hit = p.holes_begin(); hit != p.holes_end(); ++hit)
+ {
+ pwh.add_hole(*hit);
+ }
+ truncated_set.insert(pwh);
+ }
+ polygon_set = truncated_set;
+}
+} // end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+
+struct CSGCGALDomain2DImpl
+{
+ Polygon_set_2 polygon_set;
+
+ CSGCGALDomain2DImpl(){}
+ explicit CSGCGALDomain2DImpl(const Polygon_set_2& p)
+ : polygon_set(p) {}
+};
+//-----------------------------------------------------------------------------
+Polygon_2 make_circle(
+ const Circle* c,
+ const double segment_granularity
+ )
+{
+ unsigned int num_segments = 0;
+ if (c->segments() > 0)
+ {
+ num_segments = c->segments();
+ } else {
+ dolfin_assert(segment_granularity > 0.0);
+
+ num_segments = std::round(
+ (2 * DOLFIN_PI * c->radius()) / segment_granularity
+ );
+ }
+
+ // Set the minimum number of segments to 5 (to be a bit circle-like)
+ // (A polygon with less than 3 segments is degenerate)
+ num_segments = std::max(num_segments, 5u);
+
+ std::vector<Point_2> pts;
+ pts.reserve(num_segments);
+
+ for (std::size_t i = 0; i < num_segments; i++)
+ {
+ const double phi = (2*DOLFIN_PI*i) / num_segments;
+ const double x = c->center().x() + c->radius()*cos(phi);
+ const double y = c->center().y() + c->radius()*sin(phi);
+ pts.push_back(Point_2(x, y));
+ }
+
+ return Polygon_2(pts.begin(), pts.end());
+}
+//-----------------------------------------------------------------------------
+Polygon_2 make_ellipse(const Ellipse* e,
+ const double segment_granularity)
+{
+ unsigned int num_segments = 0;
+ if (e->segments() > 0) {
+ num_segments = e->segments();
+ }
+ else
+ {
+ dolfin_assert(segment_granularity > 0.0);
+ // https://en.wikipedia.org/wiki/Ellipse#Circumference
+ const double a_min_b = e->a() - e->b();
+ const double a_plus_b = e->a() + e->b();
+ const double h = (a_min_b*a_min_b) / (a_plus_b*a_plus_b);
+ const double arc_length_ellipse_approx = DOLFIN_PI * a_plus_b * (
+ 1.0 + 3.0*h / (10 + sqrt(4.0 - 3.0*h))
+ );
+ num_segments = std::round(arc_length_ellipse_approx / segment_granularity);
+ }
+
+ // A polygon with less segments than 3 is degenerate
+ // FIXME: Should this result in an empty polygon?
+ num_segments = std::max(num_segments, 3u);
+
+ std::vector<Point_2> pts;
+ pts.reserve(num_segments);
+
+ for (std::size_t i = 0; i < num_segments; i++)
+ {
+ const double phi = (2*DOLFIN_PI*i) / num_segments;
+ const double x = e->center().x() + e->a()*cos(phi);
+ const double y = e->center().y() + e->b()*sin(phi);
+ pts.push_back(Point_2(x, y));
+ }
+
+ return Polygon_2(pts.begin(), pts.end());
+}
+//-----------------------------------------------------------------------------
+Polygon_2 make_rectangle(const Rectangle* r)
+{
+ const double x0 = std::min(r->first_corner().x(), r->second_corner().x());
+ const double y0 = std::min(r->first_corner().y(), r->second_corner().y());
+
+ const double x1 = std::max(r->first_corner().x(), r->second_corner().x());
+ const double y1 = std::max(r->first_corner().y(), r->second_corner().y());
+
+ std::vector<Point_2> pts;
+ pts.push_back(Point_2(x0, y0));
+ pts.push_back(Point_2(x1, y0));
+ pts.push_back(Point_2(x1, y1));
+ pts.push_back(Point_2(x0, y1));
+
+ Polygon_2 p(pts.begin(), pts.end());
+
+ return p;
+}
+//-----------------------------------------------------------------------------
+Polygon_2 make_polygon(const Polygon* p)
+{
+ std::vector<Point_2> pts;
+ std::vector<dolfin::Point>::const_iterator v;
+ for (v = p->vertices().begin(); v != p->vertices().end(); ++v)
+ pts.push_back(Point_2(v->x(), v->y()));
+
+ return Polygon_2(pts.begin(), pts.end());
+}
+//-----------------------------------------------------------------------------
+std::unique_ptr<CSGCGALDomain2DImpl> do_transformation(const Polygon_set_2& p, Exact_Kernel::Aff_transformation_2 t)
+{
+ std::unique_ptr<CSGCGALDomain2DImpl> result(new CSGCGALDomain2DImpl);
+
+ std::list<Polygon_with_holes_2> polygon_list;
+ p.polygons_with_holes(std::back_inserter(polygon_list));
+
+ std::list<Polygon_with_holes_2>::const_iterator pit;
+ for (pit = polygon_list.begin(); pit != polygon_list.end(); ++pit)
+ {
+ const Polygon_with_holes_2& pwh = *pit;
+
+ // Transform outer boundary
+ Polygon_with_holes_2 transformed(CGAL::transform(t, pwh.outer_boundary()));
+
+ // Transform holes
+ for (Hole_const_iterator hit = pwh.holes_begin(); hit != pwh.holes_end(); hit++)
+ {
+ transformed.add_hole(CGAL::transform(t, *hit));
+ }
+
+ result->polygon_set.insert(transformed);
+ }
+
+ return result;
+}
+//-----------------------------------------------------------------------------
+CSGCGALDomain2D::CSGCGALDomain2D()
+ : impl(new CSGCGALDomain2DImpl)
+{
+
+}
+//-----------------------------------------------------------------------------
+CSGCGALDomain2D::~CSGCGALDomain2D()
+{
+}
+//-----------------------------------------------------------------------------
+CSGCGALDomain2D::CSGCGALDomain2D(std::shared_ptr<const CSGGeometry> geometry,
+ double segment_granularity)
+ : impl(new CSGCGALDomain2DImpl)
+{
+ if (geometry->dim() != 2)
+ dolfin::dolfin_error("CSGCGALDomain2D.cpp",
+ "Creating polygonal domain",
+ "Geometry has dimension %d, expected 2", geometry->dim());
+
+
+ switch (geometry->getType())
+ {
+ case CSGGeometry::Union:
+ {
+ std::shared_ptr<const CSGUnion> u = std::dynamic_pointer_cast<const CSGUnion, const CSGGeometry>(geometry);
+ dolfin_assert(u);
+
+ CSGCGALDomain2D a(u->_g0, segment_granularity);
+ CSGCGALDomain2D b(u->_g1, segment_granularity);
+
+ impl.swap(a.impl);
+ impl->polygon_set.join(b.impl->polygon_set);
+ break;
+ }
+ case CSGGeometry::Intersection:
+ {
+ auto u = std::dynamic_pointer_cast<const CSGIntersection, const CSGGeometry>(geometry);
+ dolfin_assert(u);
+
+ CSGCGALDomain2D a(u->_g0, segment_granularity);
+ CSGCGALDomain2D b(u->_g1, segment_granularity);
+
+ impl.swap(a.impl);
+ impl->polygon_set.intersection(b.impl->polygon_set);
+ break;
+ }
+ case CSGGeometry::Difference:
+ {
+ auto u = std::dynamic_pointer_cast<const CSGDifference, const CSGGeometry>(geometry);
+ dolfin_assert(u);
+ CSGCGALDomain2D a(u->_g0, segment_granularity);
+ CSGCGALDomain2D b(u->_g1, segment_granularity);
+
+ impl.swap(a.impl);
+ impl->polygon_set.difference(b.impl->polygon_set);
+ break;
+ }
+ case CSGGeometry::Translation :
+ {
+ auto t = std::dynamic_pointer_cast<const CSGTranslation, const CSGGeometry>(geometry);
+ dolfin_assert(t);
+ CSGCGALDomain2D a(t->g, segment_granularity);
+ Exact_Kernel::Aff_transformation_2 translation(CGAL::TRANSLATION, Vector_2(t->t.x(), t->t.y()));
+ std::unique_ptr<CSGCGALDomain2DImpl> transformed = do_transformation(a.impl->polygon_set, translation);
+ impl.swap(transformed);
+ break;
+ }
+ case CSGGeometry::Scaling :
+ {
+ auto t = std::dynamic_pointer_cast<const CSGScaling, const CSGGeometry>(geometry);
+ dolfin_assert(t);
+ CSGCGALDomain2D a(t->g, segment_granularity);
+ Exact_Kernel::Aff_transformation_2 tr(CGAL::IDENTITY);
+
+ // Translate if requested
+ if (t->translate)
+ tr = Exact_Kernel::Aff_transformation_2 (CGAL::TRANSLATION,
+ Vector_2(-t->c.x(), -t->c.y())) * tr;
+
+ // Do the scaling
+ tr = Exact_Kernel::Aff_transformation_2(CGAL::SCALING, t->s) * tr;
+
+ if (t->translate)
+ tr = Exact_Kernel::Aff_transformation_2(CGAL::TRANSLATION,
+ Vector_2(t->c.x(), t->c.y())) * tr;
+
+ std::unique_ptr<CSGCGALDomain2DImpl> transformed = do_transformation(a.impl->polygon_set,
+ tr);
+ impl.swap(transformed);
+ break;
+ }
+ case CSGGeometry::Rotation :
+ {
+ auto t = std::dynamic_pointer_cast<const CSGRotation, const CSGGeometry>(geometry);
+ dolfin_assert(t);
+ CSGCGALDomain2D a(t->g, segment_granularity);
+ Exact_Kernel::Aff_transformation_2 tr(CGAL::IDENTITY);
+
+ // Translate if requested
+ if (t->translate)
+ tr = Exact_Kernel::Aff_transformation_2 (CGAL::TRANSLATION,
+ Vector_2(-t->c.x(), -t->c.y())) * tr;
+
+ // Do the rotation
+ tr = Exact_Kernel::Aff_transformation_2(CGAL::ROTATION, sin(t->theta), cos(t->theta)) * tr;
+
+ if (t->translate)
+ tr = Exact_Kernel::Aff_transformation_2(CGAL::TRANSLATION,
+ Vector_2(t->c.x(), t->c.y())) * tr;
+
+ std::unique_ptr<CSGCGALDomain2DImpl> transformed = do_transformation(a.impl->polygon_set,
+ tr);
+ impl.swap(transformed);
+ break;
+ }
+ case CSGGeometry::Circle:
+ {
+ auto c = std::dynamic_pointer_cast<const Circle, const CSGGeometry>(geometry);
+ dolfin_assert(c);
+ impl->polygon_set.insert(make_circle(c.get(), segment_granularity));
+ break;
+ }
+ case CSGGeometry::Ellipse:
+ {
+ auto c = std::dynamic_pointer_cast<const Ellipse, const CSGGeometry>(geometry);
+ dolfin_assert(c);
+ impl->polygon_set.insert(make_ellipse(c.get(), segment_granularity));
+ break;
+ }
+ case CSGGeometry::Rectangle:
+ {
+ auto r = std::dynamic_pointer_cast<const Rectangle, const CSGGeometry>(geometry);
+ dolfin_assert(r);
+ impl->polygon_set.insert(make_rectangle(r.get()));
+ break;
+ }
+ case CSGGeometry::Polygon:
+ {
+ auto p = std::dynamic_pointer_cast<const Polygon, const CSGGeometry>(geometry);
+ dolfin_assert(p);
+ impl->polygon_set.insert(make_polygon(p.get()));
+ break;
+ }
+ default:
+ dolfin::dolfin_error("CSGCGALMeshGenerator2D.cpp",
+ "converting geometry to cgal polyhedron",
+ "Unhandled primitive type");
+ }
+
+ // Truncate short edges
+ // std::cout << "Creating domain, shortest edge: " << CGAL::to_double(get_shortest_edge(impl->polygon_set)) << std::endl;
+
+ truncate_short_edges(impl->polygon_set, 1e-15);
+ // std::cout << "Truncated, shortest edge: " << CGAL::to_double(get_shortest_edge(impl->polygon_set)) << std::endl;
+}
+//-----------------------------------------------------------------------------
+CSGCGALDomain2D::CSGCGALDomain2D(const CSGCGALDomain2D &other)
+ : impl(new CSGCGALDomain2DImpl(other.impl->polygon_set))
+{
+}
+//-----------------------------------------------------------------------------
+CSGCGALDomain2D &CSGCGALDomain2D::operator=(const CSGCGALDomain2D &other)
+{
+ std::unique_ptr<CSGCGALDomain2DImpl> tmp(new CSGCGALDomain2DImpl(other.impl->polygon_set));
+
+ impl.swap(tmp);
+
+ return *this;
+}
+//-----------------------------------------------------------------------------
+double CSGCGALDomain2D::compute_boundingcircle_radius() const
+{
+ std::list<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ std::vector<Point_2> points;
+
+ for (std::list<Polygon_with_holes_2>::const_iterator pit = polygon_list.begin();
+ pit != polygon_list.end(); ++pit)
+ for (Polygon_2::Vertex_const_iterator vit = pit->outer_boundary().vertices_begin();
+ vit != pit->outer_boundary().vertices_end(); ++vit)
+ points.push_back(*vit);
+
+ Min_circle min_circle (points.begin(),
+ points.end(),
+ true); //randomize point order
+
+ return sqrt(CGAL::to_double(min_circle.circle().squared_radius()));
+}
+//-----------------------------------------------------------------------------
+std::size_t CSGCGALDomain2D::num_polygons() const
+{
+ return impl->polygon_set.number_of_polygons_with_holes();
+}
+//-----------------------------------------------------------------------------
+std::vector<dolfin::Point> CSGCGALDomain2D::get_outer_polygon(std::size_t i) const
+{
+ std::vector<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ const Polygon_2& polygon = polygon_list[i].outer_boundary();
+
+ std::vector<dolfin::Point> res;
+ res.reserve(polygon.size());
+ for (std::size_t j = 0; j < polygon.size(); j++)
+ {
+ const Point_2& p = polygon.vertex(j);
+ res.push_back(dolfin::Point(CGAL::to_double(p.x()), CGAL::to_double(p.y())));
+ }
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+void CSGCGALDomain2D::join_inplace(const CSGCGALDomain2D& other)
+{
+ impl->polygon_set.join(other.impl->polygon_set);
+}
+//-----------------------------------------------------------------------------
+void CSGCGALDomain2D::difference_inplace(const CSGCGALDomain2D& other)
+{
+ impl->polygon_set.difference(other.impl->polygon_set);
+}
+//-----------------------------------------------------------------------------
+void CSGCGALDomain2D::intersect_inplace(const CSGCGALDomain2D &other,
+ double truncate_tolerance)
+{
+ if (truncate_tolerance > 0)
+ {
+ // store
+ std::set<Point_2> original_vertices;
+ {
+ std::list<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ // FIXME: For now it looks only at the outer boundary.
+ // Should take holes into account as well
+ for (const Polygon_with_holes_2& p : polygon_list)
+ {
+ const Polygon_2& bdr = p.outer_boundary();
+ for (Polygon_2::Vertex_const_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ original_vertices.insert(*vit);
+ }
+
+ }
+ }
+ // Do the actual intersection
+ impl->polygon_set.intersection(other.impl->polygon_set);
+
+ const FT shortest_edge = get_shortest_edge(impl->polygon_set);
+ const FT tolerance_squared = truncate_tolerance*truncate_tolerance;
+ if (shortest_edge < tolerance_squared)
+ {
+ truncate_short_edges(impl->polygon_set,
+ truncate_tolerance,
+ original_vertices);
+ } // end
+ }
+ else
+ {
+ impl->polygon_set.intersection(other.impl->polygon_set);
+ }
+}
+//-----------------------------------------------------------------------------
+bool CSGCGALDomain2D::point_in_domain(dolfin::Point p) const
+{
+ const Point_2 p_(p.x(), p.y());
+ return impl->polygon_set.oriented_side(p_) == CGAL::ON_POSITIVE_SIDE;
+}
+//-----------------------------------------------------------------------------
+std::string CSGCGALDomain2D::str(bool verbose) const
+{
+ std::stringstream ss;
+ const std::size_t num_polygons = impl->polygon_set.number_of_polygons_with_holes();
+ ss << "<Polygonal domain with " << num_polygons
+ << " outer polygon" << (num_polygons != 1 ? "s" : "") << std::endl;
+
+ if (verbose)
+ {
+ std::list<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+ for (const Polygon_with_holes_2& p : polygon_list)
+ {
+ ss << " Polygon ";
+ const Polygon_2& bdr = p.outer_boundary();
+ // ss << "[" << bdr.size() << "] Vertices: ";
+
+ for (Polygon_2::Vertex_const_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ ss << CGAL::to_double(vit->x()) << " " << CGAL::to_double(vit->y()) << ", ";
+ }
+ ss << std::endl;
+
+ Point_2 prev = bdr.container().back();
+ ss << "Edge lengths:";
+ for (Polygon_2::Vertex_const_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ ss << " " << sqrt(CGAL::to_double((*vit-prev).squared_length()));
+ prev = *vit;
+ }
+ ss << std::endl;
+
+
+
+ ss << "[" << p.number_of_holes() << " holes]" << std::endl;
+ }
+ }
+
+ ss << ">";
+ return ss.str();
+}
+//-----------------------------------------------------------------------------
+typedef std::map<Point_2, std::size_t>::iterator VertexMapIterator;
+typedef std::set<std::pair<std::size_t, std::size_t>>::iterator SegmentIterator;
+//-----------------------------------------------------------------------------
+VertexMapIterator pslg_split_edge(SegmentIterator si,
+ Point_2 p,
+ std::map<Point_2, std::size_t>& vertex_map,
+ std::vector<Point_2>& vertices,
+ std::set<std::pair<std::size_t, std::size_t>>& segments)
+{
+ dolfin_assert(CGAL::do_intersect(Segment_2(vertices[si->first], vertices[si->second]), p));
+ dolfin_assert(p != vertices[si->first]);
+ dolfin_assert(p != vertices[si->second]);
+
+ vertices.push_back(p);
+ const std::size_t new_vertex_index = vertex_map.size();
+ std::pair<VertexMapIterator, bool> result =
+ vertex_map.insert(std::make_pair(p, new_vertex_index));
+ dolfin_assert(result.second);
+
+ const std::size_t source_vertex_index = si->first;
+ const std::size_t target_vertex_index = si->second;
+
+ segments.erase(si);
+ segments.insert(std::make_pair(new_vertex_index, target_vertex_index));
+ segments.insert(std::make_pair(source_vertex_index, new_vertex_index));
+ return result.first;
+}
+//-----------------------------------------------------------------------------
+
+// Check if point intersects any edge in the interior
+static inline SegmentIterator
+pslg_segment_intersection(Point_2 p,
+ const std::vector<Point_2>& vertices,
+ const std::set<std::pair<std::size_t, std::size_t>>& segments)
+{
+ for (SegmentIterator it = segments.begin(); it != segments.end(); ++it)
+ {
+ const Segment_2 current(vertices[it->first], vertices[it->second]);
+ if (CGAL::do_intersect(p, current))
+ return it;
+ }
+ return segments.end();
+}
+
+//-----------------------------------------------------------------------------
+static inline void pslg_add_simple_polygon(std::map<Point_2, std::size_t>& vertex_map,
+ std::vector<Point_2>& vertices,
+ std::set<std::pair<std::size_t, std::size_t>>& segments,
+ const Polygon_2& p)
+{
+ // Note that this function assumes that all edges are sufficiently long. Short
+ // edges should be filtered out when preparing the domain (and subdomain)
+ // polygon_set_2s.
+
+ Point_2 prev = p.vertex(p.size()-1);
+
+ for (std::size_t i = 0; i < p.size(); i++)
+ {
+ const Point_2 current = p.vertex(i);
+ const Segment_2 s(prev, current);
+
+ VertexMapIterator sit = vertex_map.find(s.source());
+ if (sit == vertex_map.end())
+ {
+ // Did not find vertex
+ SegmentIterator si = pslg_segment_intersection(s.source(), vertices, segments);
+ if (si != segments.end())
+ {
+ sit = pslg_split_edge(si,
+ s.source(),
+ vertex_map,
+ vertices,
+ segments);
+ }
+ else
+ {
+ std::pair<VertexMapIterator, bool> i = vertex_map.insert(std::make_pair(s.source(), vertex_map.size()));
+ vertices.push_back(s.source());
+ dolfin_assert(i.second);
+ sit = i.first;
+ }
+ }
+
+ VertexMapIterator tit = vertex_map.find(s.target());
+ if (tit == vertex_map.end())
+ {
+ SegmentIterator si = pslg_segment_intersection(s.target(), vertices, segments);
+ if (si != segments.end())
+ {
+ tit = pslg_split_edge(si,
+ s.target(),
+ vertex_map,
+ vertices,
+ segments);
+ }
+ else
+ {
+ std::pair<VertexMapIterator, bool> i = vertex_map.insert(std::make_pair(s.target(), vertex_map.size()));
+ vertices.push_back(s.target());
+ dolfin_assert(i.second);
+ tit = i.first;
+ }
+ }
+
+ if (segments.count(std::make_pair(tit->second, sit->second)) == 0)
+ segments.insert(std::make_pair(sit->second, tit->second));
+
+ prev = current;
+ }
+}
+//-----------------------------------------------------------------------------
+std::pair<std::vector<dolfin::Point>, std::vector<std::pair<std::size_t, std::size_t>>>
+ CSGCGALDomain2D::compute_pslg(const std::vector<std::pair<std::size_t, CSGCGALDomain2D>>& domains)
+{
+ std::vector<Point_2> vertices;
+ std::map<Point_2, std::size_t> vertex_map;
+ std::set<std::pair<std::size_t, std::size_t>> segments;
+ // std::vector<Segment_2> inserted_segments;
+
+ for (const std::pair<std::size_t, CSGCGALDomain2D>& domain : domains)
+ {
+ const Polygon_set_2& p = domain.second.impl->polygon_set;
+
+ std::list<Polygon_with_holes_2> polygon_list;
+ p.polygons_with_holes(std::back_inserter(polygon_list));
+
+ for (const Polygon_with_holes_2& pwh : polygon_list)
+ {
+ pslg_add_simple_polygon(vertex_map,
+ vertices,
+ segments,
+ pwh.outer_boundary());
+
+ // Add holes
+ Hole_const_iterator hit;
+ for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit)
+ {
+ pslg_add_simple_polygon(vertex_map,
+ vertices,
+ segments,
+ *hit);
+ }
+ }
+ }
+
+ std::vector<dolfin::Point> v(vertices.size());
+ for (const std::pair<Point_2, std::size_t>& vertex : vertex_map)
+ {
+ v[vertex.second] = dolfin::Point(CGAL::to_double(vertex.first.x()), CGAL::to_double(vertex.first.y()));
+ }
+
+ std::vector<std::pair<std::size_t, std::size_t>> s(segments.begin(), segments.end());
+
+ {
+ double shortest_segment = std::numeric_limits<double>::max();
+ for (std::pair<std::size_t, std::size_t> segment : s)
+ {
+ shortest_segment = std::min(shortest_segment, (v[segment.first]-v[segment.second]).norm());
+ }
+
+ double closest_points = std::numeric_limits<double>::max();
+ for (auto i = v.begin(); i != v.end(); i++)
+ {
+ for (auto j = i+1; j != v.end(); j++)
+ {
+ closest_points = std::min(closest_points, (*i-*j).norm());
+ }
+ }
+
+ // std::cout << "Num vertices: " << v.size() << ", num edges: " << s.size() << std::endl;
+ // std::cout << "Shortest edge: " << shortest_segment << ", closest: " << closest_points << std::endl;
+ }
+
+ return std::make_pair(std::move(v), std::move(s));
+}
+//-----------------------------------------------------------------------------
+double CSGCGALDomain2D::shortest_edge() const
+{
+ FT shortest_edge = std::numeric_limits<double>::max();
+
+ std::list<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ // FIXME: For now it looks only at the outer boundary.
+ // Should take holes into account as well
+ for (const Polygon_with_holes_2& p : polygon_list)
+ {
+ const Polygon_2& bdr = p.outer_boundary();
+ Point_2 prev = bdr.container().back();
+ for (Polygon_2::Vertex_const_iterator vit = bdr.vertices_begin();
+ vit != bdr.vertices_end(); vit++)
+ {
+ shortest_edge = std::min(shortest_edge, (*vit-prev).squared_length());
+ prev = *vit;
+ }
+ }
+
+ return sqrt(CGAL::to_double(shortest_edge));
+}
+//-----------------------------------------------------------------------------
+void CSGCGALDomain2D::get_points_in_holes(std::vector<dolfin::Point>& holes) const
+{
+ std::list<Polygon_with_holes_2> polygon_list;
+ impl->polygon_set.polygons_with_holes(std::back_inserter(polygon_list));
+
+ for (const Polygon_with_holes_2& pwh : polygon_list)
+ {
+ Hole_const_iterator hit;
+ for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit)
+ {
+ const Point_2 p = point_in_polygon(*hit);
+ holes.push_back(dolfin::Point(CGAL::to_double(p.x()), CGAL::to_double(p.y())));
+ }
+ }
+}
+}
--- /dev/null
+// Copyright (C) 2012-2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#include <mshr/CSGCGALDomain3D.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGOperators.h>
+#include <mshr/CSGPrimitives3D.h>
+#include <mshr/STLFileReader.h>
+#include <mshr/OFFFileReader.h>
+#include <mshr/ASCFileReader.h>
+#include <mshr/SurfaceConsistency.h>
+#include <mshr/CSGCGALDomain2D.h>
+#include <mshr/CSGCGALMeshGenerator2D.h>
+#include <mshr/DolfinMeshUtils.h>
+#include <mshr/SurfaceReconstruction.h>
+
+#include "meshclean.h"
+#include "triangulation_refinement.h"
+#include "Polyhedron_utils.h"
+#include "smoothing.h"
+
+#include <dolfin/geometry/Point.h>
+#include <dolfin/math/basic.h>
+#include <dolfin/log/log.h>
+#include <dolfin/log/LogStream.h>
+#include <dolfin/mesh/BoundaryMesh.h>
+#include <dolfin/mesh/Vertex.h>
+#include <dolfin/mesh/Cell.h>
+
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Polyhedron_incremental_builder_3.h>
+#ifndef MSHR_ENABLE_EXPERIMENTAL
+ #include <CGAL/Nef_polyhedron_3.h>
+#else
+ #include <CGAL/corefinement_operations.h>
+#endif
+#include <CGAL/IO/Polyhedron_iostream.h>
+#include <CGAL/Origin.h>
+//#include <CGAL/Self_intersection_polyhedron_3.h>
+#include <CGAL/Polygon_mesh_processing/self_intersections.h>
+
+#include <CGAL/AABB_tree.h>
+#include <CGAL/AABB_traits.h>
+#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
+#include <CGAL/AABB_face_graph_triangle_primitive.h>
+
+#include <CGAL/convex_hull_3.h>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+#include <boost/filesystem.hpp>
+
+#include <vector>
+#include <iterator>
+#include <fstream>
+#include <iomanip>
+#include <set>
+#include <cmath>
+#include <memory>
+
+namespace
+{
+ typedef CGAL::Exact_predicates_exact_constructions_kernel Exact_Kernel;
+ typedef Exact_Kernel::Triangle_3 Exact_Triangle_3;
+ typedef Exact_Kernel::Triangle_2 Exact_Triangle_2;
+ typedef Exact_Kernel::Vector_3 Exact_Vector_3;
+ typedef CGAL::Polyhedron_3<Exact_Kernel> Exact_Polyhedron_3;
+ typedef Exact_Polyhedron_3::HalfedgeDS Exact_HalfedgeDS;
+ typedef Exact_Kernel::Point_3 Exact_Point_3;
+ typedef Exact_Kernel::Point_2 Exact_Point_2;
+ typedef Exact_Kernel::Vector_3 Vector_3;
+ typedef Exact_Kernel::Ray_3 Ray_3;
+ typedef Exact_Kernel::Aff_transformation_3 Aff_transformation_3;
+
+#ifndef MSHR_ENABLE_EXPERIMENTAL
+ typedef CGAL::Nef_polyhedron_3<Exact_Kernel> Nef_polyhedron_3;
+#endif
+
+// AABB tree primitives
+ typedef CGAL::AABB_face_graph_triangle_primitive<Exact_Polyhedron_3> Primitive;
+ typedef CGAL::AABB_traits<Exact_Kernel, Primitive> Traits;
+ typedef CGAL::AABB_tree<Traits> AABB_Tree;
+}
+
+namespace mshr
+{
+ struct CSGCGALDomain3DImpl
+ {
+ Exact_Polyhedron_3 p;
+ };
+}
+
+namespace
+{
+ //-----------------------------------------------------------------------------
+ // Convenience routine to make debugging easier. Remove before releasing.
+ template<typename Builder>
+ inline void add_triangular_facet(Builder& builder,
+ int v0, int v1, int v2)
+ {
+ builder.begin_facet();
+ builder.add_vertex_to_facet(v0);
+ builder.add_vertex_to_facet(v1);
+ builder.add_vertex_to_facet(v2);
+ builder.end_facet();
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Builder>
+ inline void add_vertex(Builder& builder,
+ const Exact_Point_3& point)
+ {
+ builder.add_vertex(point);
+ }
+ //-----------------------------------------------------------------------------
+ // Sphere
+ //-----------------------------------------------------------------------------
+ class Build_sphere : public CGAL::Modifier_base<Exact_HalfedgeDS>
+ {
+ public:
+ Build_sphere(const mshr::Sphere& sphere) : _sphere(sphere) {}
+
+ void operator()( Exact_HalfedgeDS& hds )
+ {
+ std::vector<dolfin::Point> initial_vertices { dolfin::Point( 0.0, 0.0, 1.0 ),
+ dolfin::Point( 1.0, 0.0, 0.0 ),
+ dolfin::Point( 0.0,-1.0, 0.0 ),
+ dolfin::Point(-1.0, 0.0, 0.0 ),
+ dolfin::Point( 0.0, 1.0, 0.0 ),
+ dolfin::Point( 0.0, 0.0,-1.0 )};
+
+ // Note: Some older compilers (eg. gcc on Ubuntu Precise) require std::array<std::size_t, 3> to be
+ // given explicitly in the initializer list.
+ std::vector<std::array<std::size_t, 3> > initial_triangles { std::array<std::size_t, 3>{{0, 2, 1 }},
+ std::array<std::size_t, 3>{{0, 3, 2 }},
+ std::array<std::size_t, 3>{{0, 4, 3 }},
+ std::array<std::size_t, 3>{{0, 1, 4 }},
+ std::array<std::size_t, 3>{{5, 4, 1 }},
+ std::array<std::size_t, 3>{{5, 1, 2 }},
+ std::array<std::size_t, 3>{{5, 2, 3 }},
+ std::array<std::size_t, 3>{{5, 3, 4 }} };
+
+ std::vector<dolfin::Point> vertices;
+ std::vector<std::array<std::size_t, 3> > triangles;
+ if (_sphere._segments > 1 )
+ {
+ refine_triangulation(initial_vertices,
+ initial_triangles,
+ _sphere._segments,
+ vertices,
+ triangles);
+ }
+ else
+ {
+ vertices.reserve(initial_vertices.size());
+ std::copy(initial_vertices.begin(), initial_vertices.end(), std::back_inserter(vertices));
+
+ triangles.reserve(initial_triangles.size());
+ std::copy(initial_triangles.begin(), initial_triangles.end(), std::back_inserter(triangles));
+ }
+
+ CGAL::Polyhedron_incremental_builder_3<Exact_HalfedgeDS> builder( hds, true );
+ builder.begin_surface(vertices.size(), triangles.size());
+
+ dolfin::Point center = _sphere.c;
+ for (const dolfin::Point& p : vertices)
+ {
+ const double scaling = _sphere.r/std::sqrt(p.x()*p.x() + p.y()*p.y() + p.z()*p.z());
+ add_vertex(builder, Exact_Point_3(center.x() + p.x()*scaling,
+ center.y() + p.y()*scaling,
+ center.z() + p.z()*scaling));
+ }
+
+ for (const std::array<std::size_t, 3>& t : triangles)
+ {
+ add_triangular_facet(builder, t[0], t[1], t[2]);
+ }
+
+ builder.end_surface();
+ }
+
+ private:
+ const mshr::Sphere& _sphere;
+ };
+ //-----------------------------------------------------------------------------
+ void make_sphere(const mshr::Sphere* s, Exact_Polyhedron_3& P)
+ {
+ Build_sphere builder(*s);
+ P.delegate(builder);
+ dolfin_assert(P.is_valid());
+ dolfin_assert(P.is_closed());
+ }
+ //-----------------------------------------------------------------------------
+ class Build_box : public CGAL::Modifier_base<Exact_HalfedgeDS>
+ {
+ public:
+ Build_box(const mshr::Box* box) : _box(box) {}
+
+ void operator()( Exact_HalfedgeDS& hds )
+ {
+ CGAL::Polyhedron_incremental_builder_3<Exact_HalfedgeDS> builder(hds, true);
+
+ builder.begin_surface(8, 12);
+
+ const double x0 = std::min(_box->a.x(), _box->b.x());
+ const double y0 = std::max(_box->a.x(), _box->b.x());
+
+ const double x1 = std::min(_box->a.y(), _box->b.y());
+ const double y1 = std::max(_box->a.y(), _box->b.y());
+
+ const double x2 = std::min(_box->a.z(), _box->b.z());
+ const double y2 = std::max(_box->a.z(), _box->b.z());
+
+ add_vertex(builder, Exact_Point_3(y0, x1, x2));
+ add_vertex(builder, Exact_Point_3(x0, x1, y2));
+ add_vertex(builder, Exact_Point_3(x0, x1, x2));
+ add_vertex(builder, Exact_Point_3(x0, y1, x2));
+ add_vertex(builder, Exact_Point_3(y0, x1, y2));
+ add_vertex(builder, Exact_Point_3(x0, y1, y2));
+ add_vertex(builder, Exact_Point_3(y0, y1, x2));
+ add_vertex(builder, Exact_Point_3(y0, y1, y2));
+
+ add_triangular_facet(builder, 1, 3, 2);
+ add_triangular_facet(builder, 1, 5, 3);
+ add_triangular_facet(builder, 1, 4, 5);
+ add_triangular_facet(builder, 4, 7, 5);
+ add_triangular_facet(builder, 4, 0, 7);
+ add_triangular_facet(builder, 0, 6, 7);
+ add_triangular_facet(builder, 0, 2, 6);
+ add_triangular_facet(builder, 2, 3, 6);
+ add_triangular_facet(builder, 7, 6, 5);
+ add_triangular_facet(builder, 6, 3, 5);
+ add_triangular_facet(builder, 1, 2, 4);
+ add_triangular_facet(builder, 2, 0, 4);
+
+ builder.end_surface();
+ }
+
+ const mshr::Box* _box;
+ };
+ //-----------------------------------------------------------------------------
+ void make_box(const mshr::Box* b, Exact_Polyhedron_3& P)
+ {
+ Build_box builder(b);
+ P.delegate(builder);
+ dolfin_assert(P.is_closed());
+ dolfin_assert(P.is_valid());
+ }
+ //-----------------------------------------------------------------------------
+ void make_tetrahedron(const mshr::Tetrahedron* b, Exact_Polyhedron_3& P)
+ {
+ P.make_tetrahedron(Exact_Point_3(b->a.x(), b->a.y(), b->a.z()),
+ Exact_Point_3(b->b.x(), b->b.y(), b->b.z()),
+ Exact_Point_3(b->c.x(), b->c.y(), b->c.z()),
+ Exact_Point_3(b->d.x(), b->d.y(), b->d.z()));
+ }
+ //-----------------------------------------------------------------------------
+ // Return some unit vector orthogonal to a
+ dolfin::Point generate_orthogonal(const dolfin::Point& a)
+ {
+ const dolfin::Point b(0, 1, 0);
+ const dolfin::Point c(0, 0, 1);
+
+ // Find a vector not parallel to a.
+ const dolfin::Point d = (fabs(a.dot(b)) < fabs(a.dot(c))) ? b : c;
+ const dolfin::Point orthogonal = a.cross(d);
+ return orthogonal/orthogonal.norm();
+ }
+ //-----------------------------------------------------------------------------
+ class Build_cylinder : public CGAL::Modifier_base<Exact_HalfedgeDS>
+ {
+ public:
+ Build_cylinder(const mshr::Cylinder* cylinder) : _cylinder(cylinder) {}
+
+ void operator()(Exact_HalfedgeDS& hds)
+ {
+ const dolfin::Point axis = (_cylinder->_top - _cylinder->_bottom)/(_cylinder->_top - _cylinder->_bottom).norm();
+ dolfin::Point initial = generate_orthogonal(axis);
+ dolfin_assert(dolfin::near(initial.norm(), 1.0));
+
+ CGAL::Polyhedron_incremental_builder_3<Exact_HalfedgeDS> builder(hds, true);
+
+ const int num_sides = _cylinder->_segments;
+ const bool top_degenerate = dolfin::near(_cylinder->_top_radius, 0.0);
+ const bool bottom_degenerate = dolfin::near(_cylinder->_bottom_radius, 0.0);
+
+ const int num_vertices = (top_degenerate || bottom_degenerate) ? num_sides+2 : num_sides*2+2;
+
+ builder.begin_surface(num_vertices, num_sides*4);
+
+ const double delta_theta = 2.0 * DOLFIN_PI / num_sides;
+ for (int i = 0; i < num_sides; ++i)
+ {
+ const double theta = i*delta_theta;
+ const dolfin::Point rotated = initial.rotate(axis, theta);
+ if (!bottom_degenerate)
+ {
+ const dolfin::Point p = _cylinder->_bottom + rotated*_cylinder->_bottom_radius;
+ const Exact_Point_3 p_(p.x(), p.y(), p.z());
+ add_vertex(builder, p_);
+ }
+ if (!top_degenerate)
+ {
+ const dolfin::Point p = _cylinder->_top + rotated*_cylinder->_top_radius;
+ const Exact_Point_3 p_(p.x(), p.y(), p.z());
+ add_vertex(builder, p_);
+ }
+ }
+
+ // The top and bottom vertices
+ add_vertex(builder, Exact_Point_3(_cylinder->_bottom.x(), _cylinder->_bottom.y(),
+ _cylinder->_bottom.z()));
+ add_vertex(builder, Exact_Point_3(_cylinder->_top.x(), _cylinder->_top.y(),
+ _cylinder->_top.z()));
+
+ // bottom vertex has index num_vertices-2, top vertex has index num_vertices-1
+
+ // Construct the facets on the side.
+ // Vertices must be sorted counter clockwise seen from inside.
+ for (int i = 0; i < num_sides; ++i)
+ {
+ if (top_degenerate)
+ {
+ add_triangular_facet(builder, (i + 1)%num_sides, num_vertices - 1, i);
+ }
+ else if (bottom_degenerate)
+ {
+ add_triangular_facet(builder, i, num_vertices - 1, (i + 1) % num_sides);
+ }
+ else
+ {
+ //Draw the sides as triangles.
+ const int vertex_offset = i*2;
+
+ // First triangle
+ add_triangular_facet(builder, vertex_offset, (vertex_offset + 2) % (num_sides*2), vertex_offset + 1);
+
+ // Second triangle
+ add_triangular_facet(builder, (vertex_offset + 3) % (num_sides*2), vertex_offset + 1,
+ (vertex_offset + 2) % (num_sides*2));
+ }
+ }
+
+ // Construct the bottom facet.
+ if (!bottom_degenerate)
+ {
+ for (int i = num_sides-1; i >= 0; i -= 1)
+ {
+ if (!top_degenerate)
+ {
+ add_triangular_facet(builder, num_vertices-2,( (i+1)*2) % (num_sides*2), i*2);
+ }
+ else
+ {
+ add_triangular_facet(builder, num_vertices-2, (i+1)%num_sides, i);
+ }
+ }
+ }
+
+ // Construct the the top facet
+ if (!top_degenerate)
+ {
+ for (int i = 0; i < num_sides; i++)
+ {
+ if (!bottom_degenerate)
+ {
+ add_triangular_facet(builder, num_vertices-1, i*2 + 1, ( (i+1)*2)%(num_sides*2) +1);
+ }
+ else
+ {
+ add_triangular_facet(builder, num_vertices-2, i, (i+1)%num_sides);
+ }
+ }
+ }
+
+ builder.end_surface();
+ }
+ private:
+ const mshr::Cylinder* _cylinder;
+ };
+ //-----------------------------------------------------------------------------
+ void make_cylinder(const mshr::Cylinder* c, Exact_Polyhedron_3& P)
+ {
+ Build_cylinder builder(c);
+ P.delegate(builder);
+ dolfin_assert(P.is_closed());
+ dolfin_assert(P.is_valid());
+ }
+ //-----------------------------------------------------------------------------
+ class Build_ellipsoid : public CGAL::Modifier_base<Exact_HalfedgeDS>
+ {
+ public:
+ Build_ellipsoid(const mshr::Ellipsoid& ellipsoid) : _ellipsoid(ellipsoid) {}
+
+ void operator()(Exact_HalfedgeDS& hds)
+ {
+ std::vector<dolfin::Point> initial_vertices { dolfin::Point( 0.0, 0.0, 1.0 ),
+ dolfin::Point( 1.0, 0.0, 0.0 ),
+ dolfin::Point( 0.0,-1.0, 0.0 ),
+ dolfin::Point(-1.0, 0.0, 0.0 ),
+ dolfin::Point( 0.0, 1.0, 0.0 ),
+ dolfin::Point( 0.0, 0.0,-1.0 )};
+
+ // Note: Some older compilers (eg. gcc on Ubuntu Precise) require std::array<std::size_t, 3> to be
+ // given explicitly in the initializer list.
+ std::vector<std::array<std::size_t, 3> > initial_triangles { std::array<std::size_t, 3>{{0, 2, 1 }},
+ std::array<std::size_t, 3>{{0, 3, 2 }},
+ std::array<std::size_t, 3>{{0, 4, 3 }},
+ std::array<std::size_t, 3>{{0, 1, 4 }},
+ std::array<std::size_t, 3>{{5, 4, 1 }},
+ std::array<std::size_t, 3>{{5, 1, 2 }},
+ std::array<std::size_t, 3>{{5, 2, 3 }},
+ std::array<std::size_t, 3>{{5, 3, 4 }} };
+
+ std::vector<dolfin::Point> vertices;
+ std::vector<std::array<std::size_t, 3> > triangles;
+ if (_ellipsoid._segments > 1 )
+ {
+ refine_triangulation(initial_vertices,
+ initial_triangles,
+ _ellipsoid._segments,
+ vertices,
+ triangles);
+ }
+ else
+ {
+ vertices.reserve(initial_vertices.size());
+ std::copy(initial_vertices.begin(), initial_vertices.end(), std::back_inserter(vertices));
+
+ triangles.reserve(initial_triangles.size());
+ std::copy(initial_triangles.begin(), initial_triangles.end(), std::back_inserter(triangles));
+ }
+
+ CGAL::Polyhedron_incremental_builder_3<Exact_HalfedgeDS> builder( hds, true );
+ builder.begin_surface(vertices.size(), triangles.size());
+
+ dolfin::Point center = _ellipsoid.center;
+ const double a = _ellipsoid.a, b = _ellipsoid.b, c = _ellipsoid.c;
+ for (const dolfin::Point& p : vertices)
+ {
+ const double scaling = 1.0/std::sqrt( (p.x()*p.x())/(a*a) +
+ (p.y()*p.y())/(b*b) +
+ (p.z()*p.z())/(c*c) );
+ add_vertex(builder, Exact_Point_3(center.x() + p.x()*scaling,
+ center.y() + p.y()*scaling,
+ center.z() + p.z()*scaling));
+ }
+
+ for (const std::array<std::size_t, 3>& t : triangles)
+ {
+ add_triangular_facet(builder, t[0], t[1], t[2]);
+ }
+
+ builder.end_surface();
+ }
+
+ const mshr::Ellipsoid& _ellipsoid;
+ };
+ //-----------------------------------------------------------------------------
+ void make_ellipsoid(const mshr::Ellipsoid* e, Exact_Polyhedron_3& P)
+ {
+ Build_ellipsoid builder(*e);
+ P.delegate(builder);
+ dolfin_assert(P.is_closed());
+ dolfin_assert(P.is_valid());
+ }
+ //-----------------------------------------------------------------------------
+ template <class HDS>
+ class BuildFromFacetList : public CGAL::Modifier_base<HDS>
+ {
+ public:
+ BuildFromFacetList(const std::vector<std::array<double, 3> >& vertices,
+ const std::vector<std::array<std::size_t, 3> >& facets,
+ const std::set<std::size_t>& facets_to_be_skipped)
+ : vertices(vertices), facets(facets), facets_to_be_skipped(facets_to_be_skipped){}
+ void operator()(HDS& hds)
+ {
+ std::set<std::size_t> isolated_vertices;
+ if (facets_to_be_skipped.size() > 0)
+ {
+ for (std::size_t i = 0; i < vertices.size(); i++)
+ isolated_vertices.insert(i);
+
+ for (std::size_t j = 0; j < facets.size(); j++)
+ {
+ if (facets_to_be_skipped.count(j) == 0)
+ {
+ for (auto it2 = facets[j].begin(); it2 != facets[j].end(); it2++)
+ isolated_vertices.erase(*it2);
+ }
+ }
+ }
+
+ // std::cout << "Isolated vertices: " << isolated_vertices.size() << std::endl;
+
+ CGAL::Polyhedron_incremental_builder_3<HDS> builder(hds, true);
+
+ builder.begin_surface(vertices.size(), facets.size());
+
+ for (std::size_t i = 0; i < vertices.size(); i++)
+ {
+ if (isolated_vertices.count(i) == 0)
+ builder.add_vertex(Exact_Point_3( vertices[i][0], vertices[i][1], vertices[i][2]) );
+ }
+
+ const bool has_isolated_vertices = isolated_vertices.size() > 0;
+ std::vector<std::size_t> iv(isolated_vertices.begin(), isolated_vertices.end());
+ std::sort(iv.begin(), iv.end());
+ for (std::size_t i = 0; i < facets.size(); i++)
+ {
+ if (facets_to_be_skipped.count(i) == 0)
+ {
+ if (has_isolated_vertices)
+ {
+ std::vector<std::size_t> f;
+ f.reserve(facets[i].size());
+ for (auto it = facets[i].begin(); it != facets[i].end(); it++)
+ {
+ f.push_back(*it - std::distance(iv.begin(), std::lower_bound(iv.begin(), iv.end(), *it)));
+ }
+ builder.add_facet(f.begin(), f.end());
+ }
+ else
+ builder.add_facet(facets[i].begin(), facets[i].end());
+
+ if (builder.error())
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "read surface from file",
+ "error in polyhedron builder");
+ }
+ }
+ else
+ {
+ // std::cout << "Skipping" << std::endl;
+ }
+ }
+
+ builder.end_surface();
+
+ }
+ const std::vector<std::array<double, 3> >& vertices;
+ const std::vector<std::array<std::size_t, 3> >& facets;
+ const std::set<std::size_t>& facets_to_be_skipped;
+ };
+ //-----------------------------------------------------------------------------
+ void make_surface3D(const mshr::Surface3D* s, Exact_Polyhedron_3& P)
+ {
+ dolfin_assert(s);
+
+ std::vector<std::array<double, 3> > vertices;
+ std::vector<std::array<std::size_t, 3> > facets;
+ std::set<std::size_t> skip;
+
+ if (s->_filename == "")
+ {
+ dolfin_assert(s->mesh);
+
+ std::unique_ptr<dolfin::BoundaryMesh> b;
+
+ if (s->use_cell_domain)
+ {
+ std::shared_ptr<dolfin::Mesh> m = mshr::DolfinMeshUtils::extract_subdomain(s->mesh, s->cell_domain);
+ b.reset(new dolfin::BoundaryMesh(*m, "exterior", false));
+ }
+ else
+ {
+ // Extract global boundary of mesh, order with outward pointing normals
+ b.reset(new dolfin::BoundaryMesh(*(s->mesh), "exterior", false));
+ }
+
+ for (dolfin::VertexIterator v(*b); !v.end(); ++v)
+ {
+ const dolfin::Point& p = v->point();
+ vertices.push_back(std::array<double, 3>{{p[0], p[1], p[2]}});
+ }
+
+ for (dolfin::CellIterator c(*b); !c.end(); ++c)
+ {
+ const unsigned int* vertices = c->entities(0);
+ facets.push_back(std::array<std::size_t, 3>{{vertices[0], vertices[1], vertices[2]}});
+ }
+ }
+ else
+ {
+ boost::filesystem::path fpath(s->_filename);
+ if (fpath.extension() == ".off")
+ {
+ mshr::OFFFileReader::read(s->_filename, vertices, facets);
+ }
+ else
+ {
+ if (fpath.extension() == ".stl")
+ {
+ mshr::STLFileReader::read(s->_filename, vertices, facets);
+ }
+ else if (fpath.extension() == ".asc") // ADDED
+ {
+ mshr::ASCFileReader::read(s->_filename, vertices, facets);
+ }
+ else
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "open file to read 3D surface",
+ "Unknown file type");
+ }
+
+ log(dolfin::TRACE, "Done reading file");
+ }
+
+ if (s->flip_facets)
+ {
+ log(dolfin::TRACE, "Flipping facets");
+ for (std::vector<std::array<std::size_t, 3> >::iterator it = facets.begin();
+ it != facets.end(); it++)
+ {
+ std::array<std::size_t, 3>& t = *it;
+ std::swap(t[1], t[2]);
+ }
+ }
+
+ // std::pair<std::unique_ptr<std::vector<std::array<double, 3> > >,
+ // std::unique_ptr<std::vector<std::array<std::size_t, 3> > > > filtered =
+ // SurfaceConsistency::merge_close_vertices(facets, vertices);
+
+ // log(dolfin::TRACE, "Checking connectivity");
+
+ //mshr::closest_vertices(vertices);
+
+ if (s->repair)
+ {
+ {
+ const std::size_t removed = mshr::SurfaceConsistency::remove_null_facets(facets);
+ if (removed > 0)
+ log(dolfin::TRACE, "Removed %u degenerate facets", removed);
+ }
+
+ {
+ const std::size_t removed = mshr::SurfaceConsistency::remove_isolated_vertices(vertices, facets);
+ if (removed > 0)
+ log(dolfin::TRACE, "Removed %u isolated vertices", removed);
+ }
+
+
+ // TODO: Orient all components
+ mshr::SurfaceConsistency::orient_component(facets, 0);
+ // std::size_t start_facet = s->first_facet;
+
+ std::set<std::size_t> duplicating;
+ mshr::SurfaceConsistency::checkConnectivity(facets, duplicating, false);
+ log(dolfin::TRACE, "%u facets filtered out", duplicating.size());
+ skip.insert(duplicating.begin(), duplicating.end());
+
+
+ // SurfaceConsistency::filterFacets(facets,
+ // vertices,
+ // start_facet,
+ // skip);
+
+ // std::vector<std::array<std::size_t, 3> > filtered_facets;
+ // filtered_facets.reserve(facets.size()-skip.size());
+ // for (std::size_t i = 0; i < facets.size(); i++)
+ // {
+ // if (skip.count(i) == 0)
+ // filtered_facets.push_back(facets[i]);
+ // }
+ }
+
+ // std::cout << "Duplicating: " << duplicating.size() << std::endl;
+ // for (auto it = duplicating.begin(); it != duplicating.end(); it++)
+ // std::cout << *it << " ";
+ // std::cout << std::endl;
+
+ } // end read from file
+
+ if (s->debug_dump != "")
+ {
+ log(dolfin::TRACE, "Dumping to file '%s'", s->debug_dump.c_str());
+ mshr::OFFFileReader::write(s->debug_dump,
+ vertices,
+ facets);
+ }
+
+
+ // Create the polyhedron
+ BuildFromFacetList<Exact_HalfedgeDS> builder(vertices, facets, skip);
+ P.delegate(builder);
+ log(dolfin::TRACE, "Done creating polyhedron");
+
+ if (!P.is_valid())
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "read surface from file",
+ "Polyhedron is not valid. If you are sure your file is valid, please file a bug report");
+ }
+
+ P.normalize_border();
+
+ // {
+ // std::size_t num_vertices = P.size_of_vertices();
+ // std::list<typename Exact_Polyhedron_3::Vertex_const_handle> components;
+ // mshr::PolyhedronUtils::get_disconnected_components(P, std::back_inserter(components));
+ // std::cout << "Number of components: " << components.size() << std::endl;
+ // const unsigned int deleted = P.keep_largest_connected_components(1);
+ // std::cout << "Deleted " << deleted << " disconnected components with " << num_vertices-P.size_of_vertices() << " vertices" << std::endl;
+ // std::cout << "Min vertex degree before closing: " << mshr::PolyhedronUtils::min_vertex_degree(P) << std::endl;
+ // std::cout << "Is pure triangular: " << (P.is_pure_triangle() ? "True" : "False") << std::endl;
+ // int tmp;
+ // std::cin >> tmp;
+ // }
+
+ // Triangulate polyhedron
+ //mshr::PolyhedronUtils::triangulate_polyhedron(P);
+ dolfin_assert (P.is_pure_triangle());
+
+ // remove self-intersecting facets
+ if (s->repair)
+ {
+ if (s->sharp_features_filter >= 0)
+ {
+ mshr::PolyhedronUtils::filter_sharp_features(P,
+ s->sharp_features_filter,
+ DOLFIN_PI/6.0);
+ }
+
+ // FIXME: Should this be enabled?
+ // mshr::PolyhedronUtils::remove_self_intersections(P);
+ // mshr::PolyhedronUtils::close_holes(P);
+ }
+
+ // mshr::PolyhedronUtils::list_self_intersections(P);
+
+ // if (s->degenerate_tolerance > 0)
+ // {
+ // if (remove_degenerate(P, s->degenerate_tolerance))
+ // log(dolfin::TRACE, "Removed degenerate facets from '%s'",
+ // s->_filename.c_str());
+ // }
+
+ // if (!P.is_closed())
+ // {
+ // if (s->repair)
+ // {
+ // mshr::PolyhedronUtils::cut_holes(P);
+
+ // dolfin_assert(P.is_closed());
+ // dolfin_assert(P.is_pure_triangle());
+ // remove_degenerate(P, s->degenerate_tolerance);
+ // }
+ // else
+ // {
+ // dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ // "read surface from file",
+ // "Surface is not closed.");
+ // }
+ // }
+ }
+//-----------------------------------------------------------------------------
+ template <class Polyhedron>
+ struct Insert_polyhedron_to
+ : public CGAL::Modifier_base<typename Polyhedron::HalfedgeDS>
+ {
+ Insert_polyhedron_to(const Polyhedron& in_poly)
+ : _in_poly(in_poly) {}
+
+ void operator()(typename Polyhedron::HalfedgeDS& hds)
+ {
+ typedef typename Polyhedron::HalfedgeDS HDS;
+
+ CGAL::Polyhedron_incremental_builder_3<HDS> builder(hds);
+
+ typedef typename Polyhedron::Vertex_const_iterator Vertex_const_iterator;
+ typedef typename Polyhedron::Facet_const_iterator Facet_const_iterator;
+ typedef typename Polyhedron::Halfedge_around_facet_const_circulator HFCC;
+
+ builder.begin_surface(_in_poly.size_of_vertices(),
+ _in_poly.size_of_facets(),
+ _in_poly.size_of_halfedges());
+
+ for(Vertex_const_iterator
+ vi = _in_poly.vertices_begin(), end = _in_poly.vertices_end();
+ vi != end ; ++vi)
+ {
+ builder.add_vertex(vi->point());
+ }
+
+ typedef CGAL::Inverse_index<Vertex_const_iterator> Index;
+ Index index(_in_poly.vertices_begin(), _in_poly.vertices_end());
+
+ for(Facet_const_iterator
+ fi = _in_poly.facets_begin(), end = _in_poly.facets_end();
+ fi != end; ++fi)
+ {
+ HFCC hc = fi->facet_begin();
+ HFCC hc_end = hc;
+ // std::size_t n = circulator_size( hc);
+ // CGAL_assertion( n >= 3);
+ builder.begin_facet ();
+ do
+ {
+ builder.add_vertex_to_facet(index[hc->vertex()]);
+ ++hc;
+ } while( hc != hc_end);
+ builder.end_facet();
+ }
+ builder.end_surface();
+ } // end operator()(..)
+ private:
+ const Polyhedron& _in_poly;
+ }; // end Copy_polyhedron_to<>
+//-----------------------------------------------------------------------------
+ template <class HDS>
+ class BuildExtrude2D : public CGAL::Modifier_base<HDS>
+ {
+ public:
+ BuildExtrude2D(const mshr::CSGGeometry& polygon, double z)
+ : polygon(polygon), z(z){}
+ void operator()(HDS& hds)
+ {
+ // Let the 2d mesh generator triangulate the polygon
+ std::shared_ptr<dolfin::Mesh> mesh2d(new dolfin::Mesh());
+ {
+ mshr::CSGCGALMeshGenerator2D generator;
+ generator.parameters["mesh_resolution"] = 2.0;
+ generator.parameters["partition"] = false;
+
+ const std::pair<dolfin::Point, double> bounding_circle = polygon.estimate_bounding_sphere();
+
+ // Generate 2D mesh (on all nodes if in parallel)
+ std::shared_ptr<mshr::CSGCGALDomain2D>
+ d2(new mshr::CSGCGALDomain2D(dolfin::reference_to_no_delete_pointer(polygon),
+ bounding_circle.second/6.));
+
+ mesh2d = generator.generate(d2);
+ }
+
+ CGAL::Polyhedron_incremental_builder_3<HDS> builder(hds, true);
+
+ builder.begin_surface(0, 0);
+
+ const double min_z = std::min(0., z);
+ const double max_z = std::max(0., z);
+
+ // Copy vertices to the new 3d polyhedron
+ const std::vector<double>& vertices = mesh2d->coordinates();
+ for (std::size_t i = 0; i < vertices.size()/2; i++)
+ {
+ builder.add_vertex(Exact_Point_3(vertices[2*i], vertices[2*i+1], min_z));
+ builder.add_vertex(Exact_Point_3(vertices[2*i], vertices[2*i+1], max_z));
+ }
+
+ // Add the triangles from the 2d mesh at z=0 and z=z
+ for (dolfin::CellIterator c(*mesh2d); !c.end(); ++c)
+ {
+ const unsigned int* v_indices = c->entities(0);
+ const bool flip = Exact_Triangle_2(Exact_Point_2(vertices[2*v_indices[0]], vertices[2*v_indices[0]+1]),
+ Exact_Point_2(vertices[2*v_indices[1]], vertices[2*v_indices[1]+1]),
+ Exact_Point_2(vertices[2*v_indices[2]], vertices[2*v_indices[2]+1])).orientation() == CGAL::POSITIVE;
+ builder.begin_facet();
+ builder.add_vertex_to_facet(2*v_indices[0]);
+ if (flip)
+ {
+ builder.add_vertex_to_facet(2*v_indices[2]);
+ builder.add_vertex_to_facet(2*v_indices[1]);
+ }
+ else
+ {
+ builder.add_vertex_to_facet(2*v_indices[1]);
+ builder.add_vertex_to_facet(2*v_indices[2]);
+ }
+
+ builder.end_facet();
+
+ builder.begin_facet();
+ builder.add_vertex_to_facet(2*v_indices[0]+1);
+ if (!flip)
+ {
+ builder.add_vertex_to_facet(2*v_indices[2]+1);
+ builder.add_vertex_to_facet(2*v_indices[1]+1);
+ }
+ else
+ {
+ builder.add_vertex_to_facet(2*v_indices[1]+1);
+ builder.add_vertex_to_facet(2*v_indices[2]+1);
+ }
+ builder.end_facet();
+ }
+
+ // Connect the two polygons
+ dolfin::BoundaryMesh bdr(*mesh2d, "exterior", false);
+ const dolfin::MeshFunction<std::size_t>& vertex_map = bdr.entity_map(0);
+ for (dolfin::CellIterator cell(bdr); !cell.end(); ++cell)
+ {
+ const unsigned int* v_indices = cell->entities(0);
+ builder.begin_facet();
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[0]]);
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[1]]);
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[0]]+1);
+ builder.end_facet();
+
+ builder.begin_facet();
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[1]]);
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[1]]+1);
+ builder.add_vertex_to_facet(2*vertex_map[v_indices[0]]+1);
+ builder.end_facet();
+
+ }
+
+ builder.end_surface();
+ }
+
+ const mshr::CSGGeometry& polygon;
+ const double z;
+ };
+// ----
+ void make_extrude2D(const mshr::Extrude2D* e, Exact_Polyhedron_3& P)
+ {
+ dolfin_assert(e);
+
+ //CSGCGALDomain2D polygon(e->geometry_2d.get());
+ BuildExtrude2D<Exact_HalfedgeDS> builder(*e->geometry_2d, e->z);
+ P.delegate(builder);
+ }
+//-----------------------------------------------------------------------------
+ Aff_transformation_3 get_scaling(const mshr::CSGScaling& s)
+ {
+ Aff_transformation_3 transformation(CGAL::IDENTITY);
+ if (s.translate)
+ {
+ Aff_transformation_3 translation(CGAL::TRANSLATION,
+ Vector_3(-s.c.x(),
+ -s.c.y(),
+ -s.c.z()));
+ transformation = translation * transformation;
+ }
+
+ Aff_transformation_3 scaling(CGAL::SCALING, s.s);
+ transformation = scaling * transformation;
+
+ if (s.translate)
+ {
+ Aff_transformation_3 translation(CGAL::TRANSLATION,
+ Vector_3(s.c.x(),
+ s.c.y(),
+ s.c.z()));
+ transformation = translation * transformation;
+ }
+
+ return transformation;
+ }
+//-----------------------------------------------------------------------------
+ Aff_transformation_3 get_rotation(const mshr::CSGRotation& r)
+ {
+ // Normalize rotation axis vector
+ dolfin::Point axis = r.rot_axis/(r.rot_axis.norm());
+ dolfin_assert(dolfin::near(axis.norm(), 1.0));
+
+ Aff_transformation_3 transformation(CGAL::IDENTITY);
+ if (r.translate)
+ {
+ Aff_transformation_3 translation(CGAL::TRANSLATION,
+ Vector_3(-r.c.x(),
+ -r.c.y(),
+ -r.c.z()));
+ transformation = translation * transformation;
+ }
+
+ // The Euler-Rodrigues formula
+ const double a = cos(r.theta/2);
+ const double b = -axis.x()*sin(r.theta/2);
+ const double c = -axis.y()*sin(r.theta/2);
+ const double d = -axis.z()*sin(r.theta/2);
+
+ Aff_transformation_3 rotation(a*a+b*b-c*c-d*d,
+ 2*(b*c-a*d),
+ 2*(b*d+a*c),
+ 2*(b*c+a*d),
+ a*a+c*c-b*b-d*d,
+ 2*(c*d-a*b),
+ 2*(b*d-a*c),
+ 2*(c*d+a*b),
+ a*a+d*d-b*b-c*c);
+ transformation = rotation * transformation;
+
+ if (r.translate)
+ {
+ Aff_transformation_3 translation(CGAL::TRANSLATION,
+ Vector_3(r.c.x(),
+ r.c.y(),
+ r.c.z()));
+ transformation = translation * transformation;
+ }
+
+ return transformation;
+ }
+//-----------------------------------------------------------------------------
+#ifdef MSHR_ENABLE_EXPERIMENTAL
+ bool polyline_is_degenerate(const std::vector<Exact_Point_3>& polyline, double tol)
+ {
+ double length = 0;
+ std::vector<Exact_Point_3>::const_iterator it = polyline.begin();
+ Exact_Point_3 prev = *it;
+ it++;
+ for (;it != polyline.end(); it++)
+ {
+ length += CGAL::to_double((*it-prev).squared_length());
+ }
+ length += CGAL::to_double((polyline.back()-polyline.front()).squared_length());
+
+ return length < tol;
+ }
+
+
+ typedef CGAL::Polyhedron_corefinement<Exact_Polyhedron_3> CGALCSGOperator;
+
+ void convertSubTree(const mshr::CSGGeometry* geometry, Exact_Polyhedron_3& P)
+ {
+ switch (geometry->getType())
+ {
+ case mshr::CSGGeometry::Union :
+ {
+ const mshr::CSGUnion* u = dynamic_cast<const mshr::CSGUnion*>(geometry);
+ dolfin_assert(u);
+ convertSubTree(u->_g0.get(), P);
+ Exact_Polyhedron_3 P2;
+ convertSubTree(u->_g1.get(), P2);
+
+ std::list<std::vector<Exact_Point_3> > intersection_polylines;
+ CGALCSGOperator op;
+ op(P, P2, std::back_inserter(intersection_polylines), CGALCSGOperator::Join_tag);
+
+ // Check that intersection is not degenerate
+ for (std::list<std::vector<Exact_Point_3> >::iterator it=intersection_polylines.begin();
+ it != intersection_polylines.end(); it++)
+ {
+ if (polyline_is_degenerate(*it, DOLFIN_EPS))
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "union of csg geometries",
+ "degenerate intersection polyline (geometries meet in a single point?)");
+ }
+ }
+ break;
+ }
+ case mshr::CSGGeometry::Intersection :
+ {
+ const mshr::CSGIntersection* u = dynamic_cast<const mshr::CSGIntersection*>(geometry);
+ dolfin_assert(u);
+ convertSubTree(u->_g0.get(), P);
+ Exact_Polyhedron_3 P2;
+ convertSubTree(u->_g1.get(), P2);
+
+ std::list<std::vector<Exact_Point_3> > intersection_polylines;
+ CGALCSGOperator op;
+ op(P, P2, std::back_inserter(intersection_polylines), CGALCSGOperator::Intersection_tag);
+
+ // Check that intersection is not degenerate
+ for (std::list<std::vector<Exact_Point_3> >::iterator it=intersection_polylines.begin();
+ it != intersection_polylines.end(); it++)
+ {
+ if (polyline_is_degenerate(*it, DOLFIN_EPS))
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "intersection of csg geometries",
+ "degenerate intersection polyline (geometries meet in a single point?)");
+ }
+ }
+
+ break;
+ }
+ case mshr::CSGGeometry::Difference :
+ {
+ std::cout << "Difference operator" << std::endl;
+ const mshr::CSGDifference* u = dynamic_cast<const mshr::CSGDifference*>(geometry);
+ dolfin_assert(u);
+ convertSubTree(u->_g0.get(), P);
+ Exact_Polyhedron_3 P2;
+ convertSubTree(u->_g1.get(), P2);
+
+ std::list<std::vector<Exact_Point_3> > intersection_polylines;
+ CGALCSGOperator op;
+ op(P, P2, std::back_inserter(intersection_polylines), CGALCSGOperator::P_minus_Q_tag);
+
+ std::cout << "Checking polyline" << std::endl;
+ // Check that intersection is not degenerate
+ for (std::list<std::vector<Exact_Point_3> >::iterator it=intersection_polylines.begin();
+ it != intersection_polylines.end(); it++)
+ {
+ if (polyline_is_degenerate(*it, DOLFIN_EPS))
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "difference of csg geometries",
+ "degenerate intersection polyline (geometries meet in a single point?)");
+ }
+ }
+
+ break;
+ }
+ case mshr::CSGGeometry::Translation :
+ {
+ const mshr::CSGTranslation* t = dynamic_cast<const mshr::CSGTranslation*>(geometry);
+ dolfin_assert(t);
+ convertSubTree(t->g.get(), P);
+ Aff_transformation_3 translation(CGAL::TRANSLATION, Vector_3(t->t.x(), t->t.y(), t->t.z()));
+ std::transform(P.points_begin(), P.points_end(), P.points_begin(), translation);
+ break;
+ }
+ case mshr::CSGGeometry::Scaling :
+ {
+ const mshr::CSGScaling* t = dynamic_cast<const mshr::CSGScaling*>(geometry);
+ dolfin_assert(t);
+ convertSubTree(t->g.get(), P);
+
+ Aff_transformation_3 scaling = get_scaling(*t);
+ std::transform(P.points_begin(), P.points_end(), P.points_begin(), scaling);
+ break;
+ }
+ case mshr::CSGGeometry::Rotation :
+ {
+ const mshr::CSGRotation* t = dynamic_cast<const mshr::CSGRotation*>(geometry);
+ dolfin_assert(t);
+
+ convertSubTree(t->g.get(), P);
+ Aff_transformation_3 rotation = get_rotation(*t);
+ std::transform(P.points_begin(), P.points_end(), P.points_begin(), rotation);
+ break;
+ }
+ case mshr::CSGGeometry::Cylinder :
+ {
+ const mshr::Cylinder* c = dynamic_cast<const mshr::Cylinder*>(geometry);
+ dolfin_assert(c);
+ make_cylinder(c, P);
+ break;
+ }
+ case mshr::CSGGeometry::Sphere :
+ {
+ const mshr::Sphere* s = dynamic_cast<const mshr::Sphere*>(geometry);
+ dolfin_assert(s);
+ make_sphere(s, P);
+ break;
+ }
+ case mshr::CSGGeometry::Box :
+ {
+ const mshr::Box* b = dynamic_cast<const mshr::Box*>(geometry);
+ dolfin_assert(b);
+ make_box(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::Tetrahedron :
+ {
+ const mshr::Tetrahedron* b = dynamic_cast<const mshr::Tetrahedron*>(geometry);
+ dolfin_assert(b);
+ make_tetrahedron(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::Ellipsoid :
+ {
+ const mshr::Ellipsoid* b = dynamic_cast<const mshr::Ellipsoid*>(geometry);
+ dolfin_assert(b);
+ make_ellipsoid(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::Surface3D :
+ {
+ const mshr::Surface3D* b = dynamic_cast<const mshr::Surface3D*>(geometry);
+ dolfin_assert(b);
+ make_surface3D(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::TriPolyhedron :
+ {
+ const mshr::CSGCGALDomain3D* b = dynamic_cast<const mshr::CSGCGALDomain3D*>(geometry);
+ dolfin_assert(b);
+ Insert_polyhedron_to<Exact_Polyhedron_3> inserter(b->impl->p);
+ P.delegate(inserter);
+ dolfin_assert(P.is_valid());
+ break;
+ }
+ case mshr::CSGGeometry::Extrude2D :
+ {
+ const mshr::Extrude2D* e = dynamic_cast<const mshr::Extrude2D*>(geometry);
+ dolfin_assert(e);
+ make_extrude2D(e, P);
+ break;
+ }
+ default:
+ dolfin::dolfin_error("CSGCGALDomain.cpp",
+ "converting geometry to cgal polyhedron",
+ "Unhandled primitive type");
+ }
+ }
+#else
+ std::shared_ptr<Nef_polyhedron_3>
+ convertSubTree(const mshr::CSGGeometry *geometry)
+ {
+ switch (geometry->getType())
+ {
+ case mshr::CSGGeometry::Union :
+ {
+ const mshr::CSGUnion* u = dynamic_cast<const mshr::CSGUnion*>(geometry);
+ dolfin_assert(u);
+ std::shared_ptr<Nef_polyhedron_3> g0 = convertSubTree(u->_g0.get());
+ std::shared_ptr<Nef_polyhedron_3> g1 = convertSubTree(u->_g1.get());
+ (*g0) += (*g1);
+ return g0;
+
+ break;
+ }
+ case mshr::CSGGeometry::Intersection :
+ {
+ const mshr::CSGIntersection* u = dynamic_cast<const mshr::CSGIntersection*>(geometry);
+ dolfin_assert(u);
+ std::shared_ptr<Nef_polyhedron_3> g0 = convertSubTree(u->_g0.get());
+ std::shared_ptr<Nef_polyhedron_3> g1 = convertSubTree(u->_g1.get());
+ (*g0) *= (*g1);
+ return g0;
+ break;
+ }
+ case mshr::CSGGeometry::Difference :
+ {
+ const mshr::CSGDifference* u = dynamic_cast<const mshr::CSGDifference*>(geometry);
+ dolfin_assert(u);
+ std::shared_ptr<Nef_polyhedron_3> g0 = convertSubTree(u->_g0.get());
+ std::shared_ptr<Nef_polyhedron_3> g1 = convertSubTree(u->_g1.get());
+ (*g0) -= (*g1);
+ return g0;
+ break;
+ }
+ case mshr::CSGGeometry::Translation :
+ {
+ const mshr::CSGTranslation* t = dynamic_cast<const mshr::CSGTranslation*>(geometry);
+ dolfin_assert(t);
+ std::shared_ptr<Nef_polyhedron_3> g = convertSubTree(t->g.get());
+ Aff_transformation_3 translation(CGAL::TRANSLATION, Vector_3(t->t.x(), t->t.y(), t->t.z()));
+ g->transform(translation);
+ return g;
+ break;
+ }
+ case mshr::CSGGeometry::Scaling :
+ {
+ const mshr::CSGScaling* t = dynamic_cast<const mshr::CSGScaling*>(geometry);
+ dolfin_assert(t);
+ std::shared_ptr<Nef_polyhedron_3> g = convertSubTree(t->g.get());
+ Aff_transformation_3 scaling = get_scaling(*t);
+ g->transform(scaling);
+ return g;
+ break;
+ }
+ case mshr::CSGGeometry::Rotation :
+ {
+ const mshr::CSGRotation* t = dynamic_cast<const mshr::CSGRotation*>(geometry);
+ dolfin_assert(t);
+
+ std::shared_ptr<Nef_polyhedron_3> g = convertSubTree(t->g.get());
+ Aff_transformation_3 rotation = get_rotation(*t);
+ g->transform(rotation);
+ return g;
+ break;
+ }
+ case mshr::CSGGeometry::Cylinder :
+ {
+ const mshr::Cylinder* c = dynamic_cast<const mshr::Cylinder*>(geometry);
+ dolfin_assert(c);
+ Exact_Polyhedron_3 P;
+ make_cylinder(c, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::Sphere :
+ {
+ const mshr::Sphere* s = dynamic_cast<const mshr::Sphere*>(geometry);
+ dolfin_assert(s);
+ Exact_Polyhedron_3 P;
+ make_sphere(s, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::Box :
+ {
+ const mshr::Box* b = dynamic_cast<const mshr::Box*>(geometry);
+ dolfin_assert(b);
+ Exact_Polyhedron_3 P;
+ make_box(b, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::Tetrahedron :
+ {
+ const mshr::Tetrahedron* b = dynamic_cast<const mshr::Tetrahedron*>(geometry);
+ dolfin_assert(b);
+ Exact_Polyhedron_3 P;
+ make_tetrahedron(b, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::Ellipsoid :
+ {
+ const mshr::Ellipsoid* b = dynamic_cast<const mshr::Ellipsoid*>(geometry);
+ dolfin_assert(b);
+ Exact_Polyhedron_3 P;
+ make_ellipsoid(b, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::Surface3D :
+ {
+ const mshr::Surface3D* b = dynamic_cast<const mshr::Surface3D*>(geometry);
+ dolfin_assert(b);
+ Exact_Polyhedron_3 P;
+ make_surface3D(b, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ case mshr::CSGGeometry::TriPolyhedron :
+ {
+ const mshr::CSGCGALDomain3D* b = dynamic_cast<const mshr::CSGCGALDomain3D*>(geometry);
+ dolfin_assert(b);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(b->impl->p));
+ break;
+ }
+ case mshr::CSGGeometry::Extrude2D :
+ {
+ const mshr::Extrude2D* e = dynamic_cast<const mshr::Extrude2D*>(geometry);
+ dolfin_assert(e);
+ Exact_Polyhedron_3 P;
+ make_extrude2D(e, P);
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3(P));
+ break;
+ }
+ default:
+ dolfin::dolfin_error("CSGCGALDomain.cpp",
+ "converting geometry to cgal polyhedron",
+ "Unhandled primitive type");
+ }
+ // Make compiler happy.
+ return std::shared_ptr<Nef_polyhedron_3>(new Nef_polyhedron_3);
+ }
+#endif
+//-----------------------------------------------------------------------------
+ void convert(const mshr::CSGGeometry& geometry,
+ Exact_Polyhedron_3 &P)
+ {
+ // If the tree has only one node, we don't have to convert to Nef
+ // polyhedrons for csg manipulations
+ if (!geometry.is_operator())
+ {
+ switch (geometry.getType())
+ {
+
+ case mshr::CSGGeometry::Cylinder :
+ {
+ const mshr::Cylinder* c = dynamic_cast<const mshr::Cylinder*>(&geometry);
+ dolfin_assert(c);
+ make_cylinder(c, P);
+ break;
+ }
+ case mshr::CSGGeometry::Sphere :
+ {
+ const mshr::Sphere* s = dynamic_cast<const mshr::Sphere*>(&geometry);
+ dolfin_assert(s);
+ make_sphere(s, P);
+ break;
+ }
+ case mshr::CSGGeometry::Box :
+ {
+ const mshr::Box* b = dynamic_cast<const mshr::Box*>(&geometry);
+ dolfin_assert(b);
+ make_box(b, P);
+ break;
+ }
+
+ case mshr::CSGGeometry::Tetrahedron :
+ {
+ const mshr::Tetrahedron* b = dynamic_cast<const mshr::Tetrahedron*>(&geometry);
+ dolfin_assert(b);
+ make_tetrahedron(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::Ellipsoid :
+ {
+ const mshr::Ellipsoid* b = dynamic_cast<const mshr::Ellipsoid*>(&geometry);
+ dolfin_assert(b);
+ make_ellipsoid(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::TriPolyhedron :
+ {
+ const mshr::CSGCGALDomain3D* p = dynamic_cast<const mshr::CSGCGALDomain3D*>(&geometry);
+ dolfin_assert(p);
+ Insert_polyhedron_to<Exact_Polyhedron_3> inserter(p->impl->p);
+ P.delegate(inserter);
+ dolfin_assert(P.is_valid());
+ break;
+ }
+ case mshr::CSGGeometry::Surface3D :
+ {
+ const mshr::Surface3D* b = dynamic_cast<const mshr::Surface3D*>(&geometry);
+ dolfin_assert(b);
+ make_surface3D(b, P);
+ break;
+ }
+ case mshr::CSGGeometry::Extrude2D :
+ {
+ const mshr::Extrude2D* e = dynamic_cast<const mshr::Extrude2D*>(&geometry);
+ dolfin_assert(e);
+ make_extrude2D(e, P);
+ break;
+ }
+
+ default:
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "converting geometry to cgal polyhedron",
+ "Unhandled primitive type");
+ }
+ }
+ else
+ {
+#ifdef MSHR_ENABLE_EXPERIMENTAL
+ convertSubTree(&geometry, P);
+#else
+ log(dolfin::TRACE, "Convert to nef polyhedron");
+ std::shared_ptr<Nef_polyhedron_3> cgal_geometry
+ = convertSubTree(&geometry);
+ dolfin_assert(cgal_geometry->is_valid());
+ dolfin_assert(cgal_geometry->is_simple());
+ cgal_geometry->convert_to_polyhedron(P);
+#endif
+ }
+
+ if (P.size_of_facets() == 0)
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "Convert geometry to polyhedron",
+ "Geometry contains no facet");
+ }
+
+ log(dolfin::TRACE, "Number of vertices: %d", P.size_of_vertices());
+ log(dolfin::TRACE, "Number of facets: %d", P.size_of_facets());
+ }
+ } //end unnamed namespace
+
+
+ namespace mshr
+ {
+ //-----------------------------------------------------------------------------
+ struct CSGCGALDomain3DQueryStructureImpl
+ {
+ template <typename A>
+ CSGCGALDomain3DQueryStructureImpl(A start, A end, const Exact_Polyhedron_3& p)
+ : aabb_tree(start, end, p){}
+
+ AABB_Tree aabb_tree;
+ };
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3DQueryStructure::CSGCGALDomain3DQueryStructure(std::unique_ptr<CSGCGALDomain3DQueryStructureImpl> impl)
+ : impl(std::move(impl))
+ {
+ // Do nothing
+ }
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3DQueryStructure::~CSGCGALDomain3DQueryStructure()
+ {
+ // Do nothing
+ }
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3D::CSGCGALDomain3D() : impl(new CSGCGALDomain3DImpl)
+ {
+ parameters = default_parameters();
+ }
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3D::CSGCGALDomain3D(std::shared_ptr<const mshr::CSGGeometry> csg)
+ : impl(new CSGCGALDomain3DImpl)
+ {
+ parameters = default_parameters();
+
+ if (csg->dim() != 3)
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "Creating polyhedral domain",
+ "Geometry has dimension %d, expected 3", csg->dim());
+ }
+
+ convert(*csg, impl->p);
+
+ }
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3D::CSGCGALDomain3D(const std::vector<std::array<double, 3>>& vertices,
+ const std::vector<std::array<std::size_t, 3>>& facets)
+ : impl(new CSGCGALDomain3DImpl)
+ {
+ parameters = default_parameters();
+
+ // Create the polyhedron
+ BuildFromFacetList<Exact_HalfedgeDS> builder(vertices, facets, std::set<std::size_t>{});
+ impl->p.delegate(builder);
+ }
+ //-----------------------------------------------------------------------------
+ CSGCGALDomain3D::~CSGCGALDomain3D(){}
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::insert(const CSGCGALDomain3D& p)
+ {
+ Insert_polyhedron_to<Exact_Polyhedron_3> inserter(p.impl->p);
+ impl->p.delegate(inserter);
+ CGAL_assertion(impl->p.is_valid());
+ }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_vertices() const
+ { return impl->p.size_of_vertices(); }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_facets() const
+ { return impl->p.size_of_facets(); }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_halfedges() const
+ { return impl->p.size_of_halfedges(); }
+//-----------------------------------------------------------------------------
+ double CSGCGALDomain3D::volume() const
+ {
+ double volume = .0;
+ for (Exact_Polyhedron_3::Facet_const_iterator it = impl->p.facets_begin();
+ it != impl->p.facets_end(); it++)
+ {
+ const Exact_Polyhedron_3::Halfedge_const_handle h = it->halfedge();
+ const Vector_3 V0 = h->vertex()->point()-CGAL::ORIGIN;
+ const Vector_3 V1 = h->next()->vertex()->point()-CGAL::ORIGIN;
+ const Vector_3 V2 = h->next()->next()->vertex()->point()-CGAL::ORIGIN;
+
+ volume += CGAL::to_double(V0*CGAL::cross_product(V1, V2));
+ }
+
+ return volume/6.0;
+ }
+//-----------------------------------------------------------------------------
+ std::unique_ptr<std::vector<double>> CSGCGALDomain3D::get_vertices() const
+ {
+ typedef typename Exact_Polyhedron_3::Vertex_const_iterator Vertex_const_iterator;
+
+ std::unique_ptr<std::vector<double>> vertices(new std::vector<double>());
+ vertices->reserve(impl->p.size_of_vertices());
+
+ for(Vertex_const_iterator
+ vi = impl->p.vertices_begin(), end = impl->p.vertices_end();
+ vi != end ; ++vi)
+ {
+ vertices->push_back(::CGAL::to_double( vi->point().x()));
+ vertices->push_back(::CGAL::to_double( vi->point().y()));
+ vertices->push_back(::CGAL::to_double( vi->point().z()));
+ }
+
+ return vertices;
+ }
+//-----------------------------------------------------------------------------
+ std::unique_ptr<std::vector<std::size_t>> CSGCGALDomain3D::get_facets() const
+ {
+ typedef typename Exact_Polyhedron_3::Vertex_const_iterator Vertex_const_iterator;
+ typedef typename Exact_Polyhedron_3::Facet_const_iterator Facet_const_iterator;
+ typedef typename Exact_Polyhedron_3::Halfedge_around_facet_const_circulator HFCC;
+
+ typedef CGAL::Inverse_index<Vertex_const_iterator> Index;
+ Index index(impl->p.vertices_begin(), impl->p.vertices_end());
+
+ std::unique_ptr<std::vector<std::size_t>> facets(new std::vector<std::size_t>());
+ facets->reserve(impl->p.size_of_facets());
+
+ for(Facet_const_iterator
+ fi = impl->p.facets_begin(), end = impl->p.facets_end();
+ fi != end; ++fi)
+ {
+ HFCC hc = fi->facet_begin();
+ facets->push_back(index[hc->vertex()]);
+ hc++;
+ facets->push_back(index[hc->vertex()]);
+ hc++;
+ facets->push_back(index[hc->vertex()]);
+ }
+
+ return facets;
+ }
+//-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::get_points_in_holes(std::vector<dolfin::Point>& holes,
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> q) const
+ {
+ std::vector<typename Exact_Polyhedron_3::Vertex_const_handle> parts;
+ mshr::PolyhedronUtils::get_disconnected_components(impl->p,std::back_inserter(parts));
+
+ if (parts.size() > 1)
+ {
+ if (!q)
+ q = get_query_structure();
+
+ for (std::vector<typename Exact_Polyhedron_3::Vertex_const_handle>::const_iterator it = parts.begin();
+ it != parts.end(); it++)
+ {
+ typename Exact_Polyhedron_3::Halfedge_const_handle h = (*it)->halfedge();
+ const Exact_Point_3 x1 = h->vertex()->point();
+ const Exact_Point_3 x2 = h->next()->vertex()->point();
+ const Exact_Point_3 x3 = h->next()->next()->vertex()->point();
+ const Exact_Point_3 origin( (x1.x()+x2.x()+x3.x())/3,
+ (x1.y()+x2.y()+x3.y())/3,
+ (x1.z()+x2.z()+x3.z())/3);
+
+ Vector_3 n = CGAL::cross_product(x3-x1, x2-x1);
+
+ // shoot a ray from the facet and count the number of hits
+ Ray_3 ray(origin, n);
+
+ std::list<AABB_Tree::Object_and_primitive_id> intersections;
+ q->impl->aabb_tree.all_intersections(ray, std::back_inserter(intersections));
+ // std::cout << "Number of intersections: " << intersections.size() << std::endl;
+
+ // Collect unique intersection points not including the origin point
+ std::set<Exact_Point_3> intersection_points;
+ for (std::list<AABB_Tree::Object_and_primitive_id>::const_iterator
+ iit = intersections.begin();
+ iit != intersections.end(); iit++)
+ {
+ if (const Exact_Point_3* p = CGAL::object_cast<Exact_Point_3>(&(iit->first)))
+ {
+ if (*p != origin)
+ intersection_points.insert(*p);
+ }
+ }
+
+ // std::cout << "Number of unique point intersections: " << intersection_points.size() << std::endl;
+ if (intersection_points.size() % 2 == 0)
+ {
+ // This is a hole
+ // Find a point strictly inside it
+
+ std::set<Exact_Point_3>::const_iterator pit = intersection_points.begin();
+ Exact_Point_3 closest_point = *pit;
+ Exact_Kernel::FT distance_to_closest = (closest_point-origin).squared_length();
+ pit++;
+
+ for (;pit != intersection_points.end(); pit++)
+ {
+ Exact_Kernel::FT d = (*pit-origin).squared_length();
+ if (d < distance_to_closest)
+ {
+ distance_to_closest = d;
+ closest_point = *pit;
+ }
+ }
+
+ const dolfin::Point h_point( (CGAL::to_double(origin.x())+CGAL::to_double(closest_point.x()))/2,
+ (CGAL::to_double(origin.y())+CGAL::to_double(closest_point.y()))/2,
+ (CGAL::to_double(origin.z())+CGAL::to_double(closest_point.z()))/2 );
+ // std::cout << "Point in hole: " << h_point << std::endl;
+ holes.push_back(h_point);
+ }
+ }
+ }
+ }
+//-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::remove_degenerate_facets(double tolerance)
+ {
+ remove_degenerate(impl->p, tolerance);
+ }
+//-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::ensure_meshing_preconditions()
+ {
+ if (!impl->p.is_valid())
+ dolfin::dolfin_error("CSGCGDomain3D.cpp",
+ "Checking meshing preconditions",
+ "Polyhedron is not valid");
+
+ if (parameters["remove_degenerate"])
+ remove_degenerate_facets(parameters["degenerate_tolerance"]);
+ }
+//-----------------------------------------------------------------------------
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> CSGCGALDomain3D::get_query_structure() const
+ {
+ std::unique_ptr<CSGCGALDomain3DQueryStructureImpl> i(new CSGCGALDomain3DQueryStructureImpl(faces(impl->p).first,
+ faces(impl->p).second,
+ impl->p));
+ return std::shared_ptr<CSGCGALDomain3DQueryStructure>(new CSGCGALDomain3DQueryStructure(std::move(i)));
+ }
+//-----------------------------------------------------------------------------
+ bool CSGCGALDomain3D::is_insideout() const
+ {
+ typedef Exact_Kernel::Ray_3 Exact_Ray_3;
+
+ // Pick a point on a facet a. Shoot in the direction of the normal and count
+ // the number of distinct hits (if the ray hit an edge, the same intersection
+ // point will hit sevel facets). Excluding the hit on facet a the number
+ // should be even for the polyhedron to be bounded.
+ // TODO: Take QueryStructure argument.
+ Exact_Polyhedron_3::Facet_iterator f = impl->p.facets_begin();
+
+ Exact_Ray_3 ray;
+
+ {
+ Exact_Polyhedron_3::Halfedge_handle h = f->halfedge();
+ Exact_Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ f++;
+
+ //Compute the ray
+ Exact_Vector_3 normal = CGAL::normal(t.vertex(0), t.vertex(1), t.vertex(2));
+ Exact_Point_3 mid = CGAL::centroid(t);
+
+ ray = Exact_Ray_3(mid, normal);
+ }
+
+ // Collect the points to avoid double hits on facets.
+ std::set<Exact_Point_3> points;
+ for (; f != impl->p.facets_end(); ++f)
+ {
+ Exact_Polyhedron_3::Halfedge_handle h = f->halfedge();
+ Exact_Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+
+
+ auto result = intersection(t, ray);
+ if(result)
+ {
+ if (const Exact_Point_3* p = boost::get<Exact_Point_3>(&*result))
+ {
+ points.insert(*p);
+ }
+ }
+ }
+
+ // std::cout << "Number of intersections: " << points.size() << std::endl;
+ return points.size() % 2 != 0;
+ }
+//-----------------------------------------------------------------------------
+ bool CSGCGALDomain3D::is_selfintersecting(bool verbose) const
+ {
+ if (verbose)
+ {
+ const std::string s = PolyhedronUtils::list_self_intersections(impl->p);
+ if (s != "")
+ {
+ dolfin::cout << s;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return CGAL::Polygon_mesh_processing::does_self_intersect(impl->p);
+ }
+ }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_degenerate_facets(double threshold) const
+ {
+ return number_of_degenerate_facets(impl->p, threshold);
+ }
+//-----------------------------------------------------------------------------
+ std::pair<double, double> CSGCGALDomain3D::facet_area_minmax() const
+ {
+ double smallest_triangle = std::numeric_limits<double>::max();
+ double largest_triangle = 0.;
+ for (auto f = impl->p.facets_begin(); f != impl->p.facets_end(); f++)
+ {
+ auto h = f->halfedge();
+ if (f->is_triangle())
+ {
+ const Exact_Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ const double squared_area = CGAL::to_double(t.squared_area());
+ smallest_triangle = std::min(smallest_triangle,
+ squared_area);
+ largest_triangle = std::max(largest_triangle,
+ squared_area);
+ }
+ }
+ return std::make_pair(sqrt(smallest_triangle),
+ sqrt(largest_triangle));
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::save_off(std::string filename) const
+ {
+ {
+ std::string message = "Writing to file: "+filename;
+ log(dolfin::TRACE, message);
+ }
+
+ std::ofstream outfile(filename.c_str());
+ outfile << std::setprecision(16);
+
+ outfile << impl->p;
+
+ // std::map<Exact_Polyhedron_3::Vertex_const_handle, std::size_t> vertex_map;
+ // std::size_t vertex_counter = 0;
+
+ // // Write header
+ // outfile << "OFF " << std::endl << impl->p.size_of_vertices() << " " << impl->p.size_of_facets() << " 0" << std::endl << std::endl;
+
+ // // Write vertices
+ // for (Exact_Polyhedron_3::Vertex_const_iterator vit = impl->p.vertices_begin(); vit != impl->p.vertices_end(); vit++)
+ // {
+ // vertex_map[vit] = vertex_counter;
+ // outfile << std::fixed << std::setprecision(16) << CGAL::to_double(vit->point().x()) << " "
+ // << CGAL::to_double(vit->point().y()) << " "
+ // << CGAL::to_double(vit->point().z()) << std::endl;
+ // vertex_counter++;
+ // }
+
+ // for (Exact_Polyhedron_3::Facet_const_iterator fit = impl->p.facets_begin(); fit != impl->p.facets_end(); fit++)
+ // {
+ // Exact_Polyhedron_3::Halfedge_const_iterator h = fit->halfedge();
+ // outfile << "3";
+ // outfile << vertex_map[h->vertex()] << " "
+ // << vertex_map[h->next()->vertex()] << " "
+ // << vertex_map[h->next()->next()->vertex()] << std::endl;
+ // }
+
+ outfile.close();
+ }
+//-----------------------------------------------------------------------------
+void CSGCGALDomain3D::save(std::string filename) const
+ {
+ boost::filesystem::path fpath(filename);
+ if (fpath.extension() == ".off")
+ {
+ save_off(filename);
+ }
+ else if (fpath.extension() == ".asc")
+ {
+ std::vector<std::array<double, 3>> new_vertices;
+ std::vector<std::array<std::size_t, 3>> new_facets;
+ std::vector<double> vertices = *get_vertices();
+ std::vector<std::size_t> facets = *get_facets();
+ for (std::size_t i = 0; i < vertices.size(); i += 3)
+ {
+ std::array<double, 3> vertex;
+ vertex[0] = vertices[i];
+ vertex[1] =vertices[i+1];
+ vertex[2] = vertices[i+2];
+ new_vertices.push_back(vertex);
+ }
+ for (std::size_t i = 0; i < facets.size(); i += 3)
+ {
+ std::array<std::size_t, 3> facet;
+ facet[0] = facets[i];
+ facet[1] = facets[i+1];
+ facet[2] = facets[i+2];
+ new_facets.push_back(facet);
+ }
+ mshr::ASCFileReader::write(filename,new_vertices,new_facets);
+ }
+ else if (fpath.extension() == ".stl")
+ {
+ std::vector<std::array<double, 3>> new_vertices;
+ std::vector<std::array<std::size_t, 3>> new_facets;
+ std::vector<double> vertices = *get_vertices();
+ std::vector<std::size_t> facets = *get_facets();
+ for (std::size_t i = 0; i < vertices.size(); i += 3)
+ {
+ std::array<double, 3> vertex;
+ vertex[0] = vertices[i];
+ vertex[1] =vertices[i+1];
+ vertex[2] = vertices[i+2];
+ new_vertices.push_back(vertex);
+ }
+ for (std::size_t i = 0; i < facets.size(); i += 3)
+ {
+ std::array<std::size_t, 3> facet;
+ facet[0] = facets[i];
+ facet[1] = facets[i+1];
+ facet[2] = facets[i+2];
+ new_facets.push_back(facet);
+ }
+ mshr::STLFileReader::write(filename,new_vertices,new_facets);
+ }
+ else
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D",
+ "extension to file is not known",
+ "Failed write file");
+ }
+ }
+//-----------------------------------------------------------------------------
+ std::pair<double, double> CSGCGALDomain3D::edge_length_range() const
+ {
+ Exact_Polyhedron_3::Edge_const_iterator it = impl->p.edges_begin();
+ double shortest = CGAL::to_double((it->vertex()->point() - it->opposite()->vertex()->point()).squared_length());
+ double longest = shortest;
+ it++;
+
+ for (; it != impl->p.edges_end(); it++)
+ {
+ const double l = CGAL::to_double((it->vertex()->point() - it->opposite()->vertex()->point()).squared_length());
+ shortest = std::min(shortest, l);
+ longest = std::max(longest, l);
+ }
+
+ return std::make_pair(sqrt(shortest), sqrt(longest));
+ }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_short_edges(double tolerance) const
+ {
+ std::size_t count = 0;
+ for (Exact_Polyhedron_3::Edge_const_iterator it = impl->p.edges_begin(); it != impl->p.edges_end(); it++)
+ {
+ const Exact_Kernel::FT l = (it->vertex()->point() - it->opposite()->vertex()->point()).squared_length();
+ if (l < tolerance)
+ count++;
+ }
+
+ return count;
+ }
+//-----------------------------------------------------------------------------
+ std::string CSGCGALDomain3D::str(bool verbose) const
+ {
+ std::stringstream ss;
+ ss << "Triangular polyhedron with" << std::endl;
+ ss << " " << num_vertices() << " vertices," << std::endl;
+ ss << " " << num_facets() << " facets," << std::endl;
+ ss << " " << num_halfedges() << " halfedges." << std::endl;
+
+ if (verbose)
+ {
+ ss << "Volume: " << volume() << std::endl;
+ const std::pair<double, double> edge_lengths = edge_length_range();
+ ss << "Edge length range: (" << edge_lengths.first << ", " << edge_lengths.second << ")" << std::endl;
+ ss << "Degenerate facets: " << num_degenerate_facets(1e-12) << std::endl;
+ ss << "Is inside out: " << (is_insideout() ? "Yes" : "No") << std::endl;
+ ss << "Is self-intersecting: " << (is_selfintersecting() ? "Yes" : "No") << std::endl;
+ ss << "Is closed: " << (num_holes() > 0 ? "Yes" : "No");
+ }
+
+ return ss.str();
+ }
+//-----------------------------------------------------------------------------
+ std::shared_ptr<CSGCGALDomain3D> CSGCGALDomain3D::convex_hull() const
+ {
+ std::shared_ptr<CSGCGALDomain3D> res(new CSGCGALDomain3D);
+ CGAL::convex_hull_3(impl->p.points_begin(), impl->p.points_end(), res->impl->p);
+
+ return res;
+ }
+ //-----------------------------------------------------------------------------
+ std::shared_ptr<CSGCGALDomain3D> CSGCGALDomain3D::convex_hull(const std::vector<std::array<double, 3>>& point_set)
+ {
+ std::vector<Exact_Point_3> points;
+ points.reserve(point_set.size());
+ for (const std::array<double,3>& p : point_set)
+ points.push_back(Exact_Point_3(p[0], p[1], p[2]));
+
+ std::shared_ptr<CSGCGALDomain3D> res(new CSGCGALDomain3D);
+ CGAL::convex_hull_3(points.begin(),
+ points.end(),
+ res->impl->p);
+
+ return res;
+ }
+//-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::filter_facets(dolfin::Point start,
+ double threshold,
+ std::shared_ptr<CSGCGALDomain3DQueryStructure> q)
+ {
+ std::cout << "Filtering facets" << std::endl;
+
+ if (!q)
+ q = get_query_structure();
+
+ typedef AABB_Tree::Point_and_primitive_id Point_and_primitive_id;
+ typedef Exact_Polyhedron_3::Face_handle Face_handle;
+ typedef Exact_Polyhedron_3::Halfedge_handle Halfedge_handle;
+ Exact_Point_3 query_point(start[0], start[1], start[2]);
+ Point_and_primitive_id pp = q->impl->aabb_tree.closest_point_and_primitive(query_point);
+ dolfin::log(dolfin::TRACE, "Closest point: (%f, %f, %f)",
+ CGAL::to_double(pp.first.x()),
+ CGAL::to_double(pp.first.y()),
+ CGAL::to_double(pp.first.z()));
+
+ const double cos_threshold = cos(threshold);
+
+ std::set<Face_handle> to_be_removed;
+ {
+ std::deque<Face_handle> queue;
+ queue.push_back(pp.second);
+
+ while (!queue.empty())
+ {
+ Face_handle f = queue.front();
+ queue.pop_front();
+
+ if (to_be_removed.count(f) > 0)
+ continue;
+
+ to_be_removed.insert(f);
+
+ const Halfedge_handle start = f->halfedge();
+ Halfedge_handle c = start;
+ const Exact_Triangle_3 f_triangle = PolyhedronUtils::get_facet_triangle<Exact_Polyhedron_3>(f->halfedge());
+ do
+ {
+ Halfedge_handle current = c;
+ c = c->next();
+
+ if (current->is_border_edge())
+ continue;
+
+ Face_handle f2 = current->opposite()->facet();
+ if (PolyhedronUtils::get_triangle_cos_angle<Exact_Triangle_3>(f_triangle,
+ PolyhedronUtils::get_facet_triangle<Exact_Polyhedron_3>(f2->halfedge())) > cos_threshold &&
+ to_be_removed.count(f2) == 0)
+ {
+ queue.push_back(f2);
+ }
+
+ } while (c != start);
+ }
+ }
+
+ dolfin::log(dolfin::TRACE, "Removing %d facets", to_be_removed.size());
+
+ for (auto fit = to_be_removed.begin(); fit != to_be_removed.end(); fit++)
+ {
+ impl->p.erase_facet((*fit)->halfedge());
+ }
+
+ dolfin_assert(impl->p.is_valid());
+ }
+//-----------------------------------------------------------------------------
+ // std::shared_ptr<CSGCGALDomain3D> get_component(dolfin::Point p,
+ // std::shared_ptr<CSGCGALDomain3DQueryStructure> qs)
+ // {
+ // if (!qs)
+ // qs = get_query_structure();
+
+
+ // typedef AABB_Tree::Point_and_primitive_id Point_and_primitive_id;
+ // typedef Exact_Polyhedron_3::Face_handle Face_handle;
+ // typedef Exact_Polyhedron_3::Halfedge_handle Halfedge_handle;
+ // Exact_Point_3 query_point(start[0], start[1], start[2]);
+ // Point_and_primitive_id pp = q->impl->aabb_tree.closest_point_and_primitive(query_point);
+ // dolfin::log(dolfin::TRACE, "Closest point: %s", dolfin::Point(CGAL::double(pp.first[0])),
+ // dolfin::Point(CGAL::to_double(pp.first[1])),
+ // dolfin::Point(CGAL::to_double(pp.first[2])));
+
+ // const double cos_threshold = cos(threshold);
+
+ // std::set<Face_handle> to_be_removed;
+ // {
+ // std::deque<Face_handle> queue;
+ // queue.push_back(pp.second);
+
+ // while (!queue.empty())
+ // {
+ // Face_handle f = queue.front();
+ // queue.pop_front();
+
+ // if (to_be_removed.count(f) > 0)
+ // continue;
+
+ // to_be_removed.insert(f);
+
+ // const Halfedge_handle start = f->halfedge();
+ // Halfedge_handle c = start;
+ // const Exact_Triangle_3 f_triangle = PolyhedronUtils::get_facet_triangle<Exact_Polyhedron_3>(f->halfedge());
+ // do
+ // {
+ // Halfedge_handle current = c;
+ // c = c->next();
+
+ // if (current->is_border_edge())
+ // continue;
+
+ // Face_handle f2 = current->opposite()->facet();
+ // if (PolyhedronUtils::get_triangle_cos_angle<Exact_Triangle_3>(f_triangle,
+ // PolyhedronUtils::get_facet_triangle<Exact_Polyhedron_3>(f2->halfedge())) > cos_threshold &&
+ // to_be_removed.count(f2) == 0)
+ // {
+ // queue.push_back(f2);
+ // }
+
+ // } while (c != start);
+ // }
+ // }
+
+ // std::cout << "Removing " << to_be_removed.size() << " facets" << std::endl;
+
+ // for (auto fit = to_be_removed.begin(); fit != to_be_removed.end(); fit++)
+ // {
+ // impl->p.erase_facet((*fit)->halfedge());
+ // }
+
+ // dolfin_assert(impl->p.is_valid());
+
+ // }
+//-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::inside_out()
+ {
+ dolfin_assert(impl->p.is_valid());
+ impl->p.inside_out();
+ }
+//-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_holes() const
+ {
+ impl->p.normalize_border();
+
+ dolfin_assert(impl->p.is_valid(false, 0));
+
+ const std::vector<Exact_Polyhedron_3::Halfedge_handle> holes = PolyhedronUtils::get_holes(impl->p);
+ return holes.size();
+ }
+ //-----------------------------------------------------------------------------
+ bool CSGCGALDomain3D::close_hole(std::size_t hole, std::string method)
+ {
+ dolfin::warning("Hole closing is an experimental feature");
+ dolfin_assert(impl->p.is_valid(false, 0));
+ impl->p.normalize_border();
+
+ const std::vector<Exact_Polyhedron_3::Halfedge_handle> holes = PolyhedronUtils::get_holes(impl->p);
+
+ if (method == "auto")
+ PolyhedronUtils::close_hole(impl->p,
+ holes[hole]);
+ else if (method == "planar")
+ {
+ const bool res = PolyhedronUtils::triangulate_polygon_3d(impl->p,
+ holes[hole],
+ false);
+
+ dolfin_assert(impl->p.is_valid(false, 0));
+ //dolfin_assert(impl->p.is_pure_triangle());
+ impl->p.normalize_border();
+ return res;
+ }
+ else if (method == "split")
+ {
+ const bool res = PolyhedronUtils::split_hole_planar(impl->p, holes[hole]);
+ dolfin_assert(impl->p.is_valid(false, 0));
+ //dolfin_assert(impl->p.is_pure_triangle());
+ impl->p.normalize_border();
+ return res;
+ }
+ else
+ {
+ dolfin::dolfin_error("CSGCGALDomain3D.cpp",
+ "triangulate hole",
+ "Unknown hole closing method");
+ return false;
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::list_hole(std::size_t hole) const
+ {
+ const std::vector<Exact_Polyhedron_3::Halfedge_handle> holes = PolyhedronUtils::get_holes(impl->p);
+ PolyhedronUtils::list_hole<Exact_Polyhedron_3>(holes[hole]);
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::close_holes()
+ {
+ dolfin::warning("Hole closing is an experimental feature");
+
+ const std::vector<Exact_Polyhedron_3::Halfedge_handle> holes = PolyhedronUtils::get_holes(impl->p);
+
+ std::size_t counter = 0;
+ for (const Exact_Polyhedron_3::Halfedge_handle h : holes)
+ {
+ // save_off("not_intersecting.off");
+ PolyhedronUtils::close_hole(impl->p, h);
+
+ counter++;
+
+ dolfin_assert(impl->p.is_valid(false, 0));
+ dolfin_assert(impl->p.is_pure_triangle());
+ }
+
+ dolfin_assert(impl->p.is_closed());
+ }
+ //-----------------------------------------------------------------------------
+ std::shared_ptr<CSGCGALDomain3D> CSGCGALDomain3D::reconstruct_surface(double expansion) const
+ {
+ std::vector<std::array<double, 3>> new_vertices;
+ std::vector<std::array<std::size_t, 3>> new_facets;
+
+ {
+ std::unique_ptr<const std::vector<double>> vertices = get_vertices();
+ std::unique_ptr<const std::vector<std::size_t>> facets = get_facets();
+
+ SurfaceReconstruction::reconstruct(*vertices,
+ *facets,
+ new_vertices,
+ new_facets,
+ expansion);
+ }
+
+ return std::shared_ptr<CSGCGALDomain3D>(new CSGCGALDomain3D(new_vertices,
+ new_facets));
+ }
+ //-----------------------------------------------------------------------------
+ std::shared_ptr<CSGCGALDomain3D> CSGCGALDomain3D::remesh_surface(double edge_length,
+ double sharp_edge_tolerance) const
+ {
+ std::vector<std::array<double, 3>> remeshed_vertices;
+ std::vector<std::array<std::size_t, 3>> remeshed_facets;
+
+ std::unique_ptr<std::vector<double>> vertices = get_vertices();
+ std::unique_ptr<std::vector<std::size_t>> facets = get_facets();
+
+ SurfaceReconstruction::remesh(edge_length,
+ sharp_edge_tolerance,
+ *vertices,
+ *facets,
+ remeshed_vertices,
+ remeshed_facets);
+ return std::shared_ptr<CSGCGALDomain3D>(new CSGCGALDomain3D(remeshed_vertices,
+ remeshed_facets));
+ }
+ //-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::remove_selfintersections()
+ {
+ return PolyhedronUtils::remove_self_intersections(impl->p);
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::normalize()
+ {
+ typedef typename Exact_Polyhedron_3::Vertex_iterator Vertex_iterator;
+ typedef typename Exact_Kernel::Aff_transformation_3 Aff_transformation_3;
+
+ if (impl->p.size_of_vertices() > 0)
+ {
+ Vertex_iterator v = impl->p.vertices_begin();
+ const Vertex_iterator end = impl->p.vertices_end();
+ CGAL::Bbox_3 bbox = v->point().bbox();
+ v++;
+
+ for (; v != end; v++)
+ {
+ bbox += v->point().bbox();
+ }
+
+ std::cout << "Bounding box: " << bbox << std::endl;
+ const Exact_Point_3 mid((bbox.xmax()+bbox.xmin())/2,
+ (bbox.ymax()+bbox.ymin())/2,
+ (bbox.zmax()+bbox.zmin())/2);
+ std::cout << "Mid point: " << mid << std::endl;
+ const Exact_Vector_3 translation = CGAL::ORIGIN-mid;
+ const Exact_Kernel::RT scaling = std::max(std::max(bbox.xmax()-bbox.xmin(),
+ bbox.ymax()-bbox.ymin()),
+ bbox.zmax()-bbox.zmin());
+
+ std::cout << "Scaling: " << scaling << std::endl;
+
+ const Aff_transformation_3 t(CGAL::Translation(), translation);
+ const Aff_transformation_3 s(CGAL::Scaling(), 2/scaling);
+ const Aff_transformation_3 transform = s*t;
+
+ CGAL::Bbox_3 new_bbox = Exact_Point_3(0,0,0).bbox();
+
+ for (Vertex_iterator v = impl->p.vertices_begin(); v != end; v++)
+ {
+ //v->point() = v->point() + translation;
+ v->point() = transform.transform(v->point());
+ new_bbox += v->point().bbox();
+ }
+
+ std::cout << "New bbox: " << new_bbox << std::endl;
+ }
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::smooth_taubin(std::size_t iterations)
+ {
+ for (std::size_t i = 0; i < iterations; i++)
+ {
+ LaplacianSmoothing::smooth(impl->p, .8);
+ LaplacianSmoothing::smooth(impl->p, -.805);
+ }
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::smooth_laplacian(double c)
+ {
+ LaplacianSmoothing::smooth(impl->p, c);
+ }
+ //-----------------------------------------------------------------------------
+ double CSGCGALDomain3D::sharpest_edge() const
+ {
+ return PolyhedronUtils::sharpest_edge(impl->p);
+ }
+ //-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_sharp_edges(double tolerance) const
+ {
+ return 0;
+ }
+ //-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::num_disconnected_components() const
+ {
+ std::vector<typename Exact_Polyhedron_3::Vertex_const_handle> parts;
+ mshr::PolyhedronUtils::get_disconnected_components(impl->p, std::back_inserter(parts));
+ return parts.size();
+ }
+ //-----------------------------------------------------------------------------
+ std::size_t CSGCGALDomain3D::keep_largest_components(std::size_t n)
+ {
+ return impl->p.keep_largest_connected_components(static_cast<unsigned int>(n));
+ }
+ //-----------------------------------------------------------------------------
+ std::pair<dolfin::Point, dolfin::Point> CSGCGALDomain3D::get_aabb() const
+ {
+ std::pair<Exact_Point_3, Exact_Point_3> aabb = PolyhedronUtils::get_aabb(impl->p);
+ auto res = std::make_pair(dolfin::Point(CGAL::to_double(aabb.first[0]),
+ CGAL::to_double(aabb.first[1]),
+ CGAL::to_double(aabb.first[2])),
+ dolfin::Point(CGAL::to_double(aabb.second[0]),
+ CGAL::to_double(aabb.second[1]),
+ CGAL::to_double(aabb.second[2])));
+ return res;
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::refine_center_vertex()
+ {
+ PolyhedronUtils::center_vertex_refinement(impl->p);
+ }
+ //-----------------------------------------------------------------------------
+ void CSGCGALDomain3D::refine_edge_split()
+ {
+ PolyhedronUtils::split_edge_refinement(impl->p);
+ }
+ //-----------------------------------------------------------------------------
+} // end namespace mshr
+
--- /dev/null
+// Copyright (C) 2012 Johannes Ring, 2012-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+
+#include <vector>
+#include <cmath>
+#include <limits>
+#include <fstream>
+
+#ifndef CGAL_HEADER_ONLY
+#include <CGAL/compiler_config.h>
+#endif
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Triangulation_vertex_base_with_info_2.h>
+#include <CGAL/Constrained_Delaunay_triangulation_2.h>
+#include <CGAL/Triangulation_face_base_with_info_2.h>
+#include <CGAL/Delaunay_mesher_2.h>
+#include <CGAL/Delaunay_mesh_face_base_2.h>
+#include <CGAL/Delaunay_mesh_vertex_base_2.h>
+#include <CGAL/Delaunay_mesh_size_criteria_2.h>
+#include <CGAL/lloyd_optimize_mesh_2.h>
+
+#include <dolfin/common/constants.h>
+#include <dolfin/common/MPI.h>
+#include <dolfin/math/basic.h>
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/mesh/MeshEditor.h>
+#include <dolfin/mesh/CellType.h>
+#include <dolfin/mesh/MeshFunction.h>
+#include <dolfin/mesh/MeshDomains.h>
+#include <dolfin/mesh/MeshValueCollection.h>
+#include <dolfin/mesh/MeshPartitioning.h>
+#include <dolfin/log/log.h>
+
+#include <mshr/CSGCGALMeshGenerator2D.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGOperators.h>
+#include <mshr/CSGPrimitives2D.h>
+#include <mshr/CSGCGALDomain2D.h>
+
+
+typedef CGAL::Exact_predicates_inexact_constructions_kernel Inexact_Kernel;
+typedef CGAL::Delaunay_mesh_vertex_base_2<Inexact_Kernel> Vertex_base;
+typedef CGAL::Delaunay_mesh_face_base_2<Inexact_Kernel> Face_base;
+typedef CGAL::Triangulation_data_structure_2<Vertex_base, Face_base> TDS;
+typedef CGAL::Constrained_Delaunay_triangulation_2<Inexact_Kernel, TDS> CDT;
+typedef CGAL::Delaunay_mesh_size_criteria_2<CDT> Mesh_criteria_2;
+typedef CGAL::Delaunay_mesher_2<CDT, Mesh_criteria_2> CGAL_Mesher_2;
+
+typedef CDT::Vertex_handle Vertex_handle;
+typedef CDT::Face_handle Face_handle;
+typedef Inexact_Kernel::Point_2 Point_2;
+
+namespace mshr
+{
+
+//-----------------------------------------------------------------------------
+CSGCGALMeshGenerator2D::CSGCGALMeshGenerator2D()
+{
+ parameters = default_parameters();
+}
+//-----------------------------------------------------------------------------
+CSGCGALMeshGenerator2D::~CSGCGALMeshGenerator2D() {}
+//-----------------------------------------------------------------------------
+void explore_subdomain(const CDT& cdt,
+ Face_handle f,
+ std::size_t marker,
+ std::map<Face_handle, unsigned int>& subdomain_map,
+ std::set<Face_handle>& not_visited)
+{
+ std::deque<Face_handle> queue;
+ queue.push_back(f);
+
+ while (!queue.empty())
+ {
+ CDT::Face_handle face = queue.front();
+ queue.pop_front();
+
+ std::set<Face_handle>::iterator face_not_visited = not_visited.find(face);
+ if (face_not_visited != not_visited.end())
+ {
+ dolfin_assert(subdomain_map.find(face) == subdomain_map.end());
+ not_visited.erase(face_not_visited);
+ subdomain_map[face] = marker;
+
+ for(int i = 0; i < 3; i++)
+ {
+ const CDT::Edge e(face,i);
+ if (!cdt.is_constrained(e))
+ queue.push_back(face->neighbor(i));
+ }
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// Set the member in_domain and counter for all faces in the cdt
+std::map<Face_handle, unsigned int>
+ explore_subdomains(const CDT& cdt,
+ const CSGCGALDomain2D& total_domain,
+ const std::vector<std::pair<std::size_t, CSGCGALDomain2D>>&
+ subdomain_geometries)
+{
+ std::set<Face_handle> not_visited;
+
+ for(CDT::Finite_faces_iterator fit = cdt.finite_faces_begin();
+ fit != cdt.finite_faces_end(); ++fit)
+ {
+ if (fit->is_in_domain())
+ not_visited.insert(fit);
+ }
+
+ std::map<Face_handle, unsigned int> subdomain_map;
+
+ std::list<CDT::Face_handle> queue;
+ queue.push_back(*not_visited.begin());
+
+ while (!not_visited.empty())
+ {
+ const Face_handle f = *not_visited.begin();
+
+ // set to default domain (0)
+ unsigned int marker = 0;
+
+ const Point_2 p0 = f->vertex(0)->point();
+ const Point_2 p1 = f->vertex(1)->point();
+ const Point_2 p2 = f->vertex(2)->point();
+
+ const dolfin::Point p( (p0[0] + p1[0] + p2[0]) / 3.0,
+ (p0[1] + p1[1] + p2[1]) / 3.0 );
+
+ for (const std::pair<std::size_t, CSGCGALDomain2D> subdomain : subdomain_geometries)
+ {
+ if (subdomain.second.point_in_domain(p))
+ {
+ marker = subdomain.first;
+ break;
+ }
+ }
+
+ explore_subdomain(cdt,
+ f,
+ marker,
+ subdomain_map,
+ not_visited);
+ }
+
+ return subdomain_map;
+}
+//-----------------------------------------------------------------------------
+double shortest_constrained_edge(const CDT &cdt)
+{
+ double min_length = std::numeric_limits<double>::max();
+ for (CDT::Finite_edges_iterator it = cdt.finite_edges_begin();
+ it != cdt.finite_edges_end();
+ it++)
+ {
+ if (!cdt.is_constrained(*it))
+ continue;
+
+ // An edge is an std::pair<Face_handle, int>
+ // see CGAL/Triangulation_data_structure_2.h
+ CDT::Face_handle f = it->first;
+ const int i = it->second;
+
+ CDT::Point p1 = f->vertex( (i+1)%3 )->point();
+ CDT::Point p2 = f->vertex( (i+2)%3 )->point();
+
+ min_length = std::min(CGAL::to_double((p1-p2).squared_length()),
+ min_length);
+ }
+
+ return min_length;
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh> CSGCGALMeshGenerator2D::generate(const std::shared_ptr<const CSGCGALDomain2D> total_domain,
+ const std::vector<std::pair<std::size_t,
+ std::shared_ptr<const CSGCGALDomain2D>>>& subdomains)
+{
+ const bool partition = parameters["partition"];
+
+ std::shared_ptr<dolfin::Mesh> mesh(new dolfin::Mesh);
+
+ // Note that if not in parallel (ie. size() == 0)
+ // then both receiver and broadcaster will return false
+ // If not partition, then generate the mesh on all processes
+ if (!partition || !dolfin::MPI::is_receiver(mesh->mpi_comm()))
+ {
+ double cell_size;
+ {
+ const double mesh_resolution = parameters["mesh_resolution"];
+ if (mesh_resolution > 0)
+ {
+ const double min_radius = total_domain->compute_boundingcircle_radius();
+ cell_size = 2.0*min_radius/mesh_resolution;
+ }
+ else
+ cell_size = parameters["cell_size"];
+ }
+
+ std::vector<std::pair<std::size_t, CSGCGALDomain2D> >
+ subdomain_geometries;
+
+ log(dolfin::TRACE, "Converting geometry to CGAL polygon");
+
+ // Empty polygon, will be populated when traversing the subdomains
+ CSGCGALDomain2D overlaying;
+
+ if (!subdomains.empty())
+ log(dolfin::TRACE, "Processing subdomains");
+
+ // Add the subdomains to the PSLG. Traverse in reverse order to get the latest
+ // added subdomain on top
+ for (auto rit = subdomains.rbegin(); rit != subdomains.rend(); rit++)
+ {
+ const std::size_t current_index = rit->first;
+ CSGCGALDomain2D cgal_geometry(*(rit->second));
+
+ // Only the part inside the total domain
+ cgal_geometry.intersect_inplace(*total_domain, 1e-15);
+
+ // Only the part outside overlaying subdomains
+ cgal_geometry.difference_inplace(overlaying);
+
+ subdomain_geometries.push_back(std::make_pair(current_index,
+ cgal_geometry));
+
+ overlaying.join_inplace(cgal_geometry);
+ }
+
+ CSGCGALDomain2D remaining(*total_domain);
+ remaining.difference_inplace(overlaying);
+
+ subdomain_geometries.push_back(std::make_pair(0, remaining));
+
+ const auto pslg = CSGCGALDomain2D::compute_pslg(subdomain_geometries);
+
+
+ // for (dolfin::Point p : pslg.first)
+ // {
+ // std::cout << "\"Point " << p.x() << " " << p.y() << "\" ";
+ // }
+ // std::cout << std::endl;
+
+ // for (std::pair<std::size_t, std::size_t> s : pslg.second)
+ // {
+ // dolfin::Point p0 = pslg.first[s.first];
+ // dolfin::Point p1 = pslg.first[s.second];
+ // std::cout << "\"Segment " << p0.x() << " " << p0.y() << ", " << p1.x() << " " << p1.y() << "\" ";
+ // }
+ // std::cout << std::endl;
+
+ // Create empty CGAL triangulation and copy data from the PSLG
+ CDT cdt;
+
+ {
+ std::vector<CDT::Vertex_handle> vertices;
+ {
+ // Insert the vertices into the triangulation
+ vertices.reserve(pslg.first.size());
+ for (const dolfin::Point& vertex : pslg.first)
+ {
+ const Point_2 p(vertex.x(), vertex.y());
+ vertices.push_back(cdt.insert(p));
+ }
+ }
+
+ // Insert the edges as constraints
+ for (const std::pair<std::size_t, size_t>& edge : pslg.second)
+ {
+ cdt.insert_constraint(vertices[edge.first], vertices[edge.second]);
+ }
+ }
+
+ log(dolfin::TRACE, "Initializing mesh refinement");
+
+ // Create mesher
+ CGAL_Mesher_2 mesher(cdt);
+
+ // Add seeds for all faces in the total domain
+ std::vector<dolfin::Point> hole_points;
+ total_domain->get_points_in_holes(hole_points);
+ std::vector<Point_2> seed_points;
+ for (dolfin::Point p : hole_points)
+ seed_points.push_back(Point_2(p.x(), p.y()));
+
+ log(dolfin::TRACE, "Added %d seed points", seed_points.size());
+ mesher.set_seeds(seed_points.begin(), seed_points.end(), false);
+
+ // Set shape and size criteria
+ const double shape_bound = parameters["triangle_shape_bound"];
+ mesher.set_criteria(Mesh_criteria_2(shape_bound, cell_size));
+
+ // Refine CGAL mesh/triangulation
+ mesher.refine_mesh();
+
+ // Lloyd optimization
+ if (parameters["lloyd_optimize"]) {
+ log(dolfin::TRACE, "Optimizing mesh by Lloyd smoothing");
+ CGAL::lloyd_optimize_mesh_2(cdt);
+ }
+
+ // Make sure triangulation is valid
+ dolfin_assert(cdt.is_valid());
+
+ // Mark the subdomains
+ log(dolfin::TRACE, "Exploring subdomains in mesh");
+
+ std::map<Face_handle, unsigned int> subdomain_map =
+ explore_subdomains(cdt, *total_domain, subdomain_geometries);
+
+ // Clear mesh
+ // This function has been removed from dolfin.
+ // mesh.clear();
+
+ const std::size_t num_vertices = cdt.number_of_vertices();
+
+ // Count valid cells
+ std::size_t num_cells = 0;
+ for (CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ // Add cell if it is in the domain
+ if (cgal_cell->is_in_domain())
+ {
+ num_cells++;
+ }
+ }
+
+ log(dolfin::DBG, "Mesh with %d vertices and %d cells created", num_vertices, num_cells);
+
+ // Create a MeshEditor and open
+ dolfin::MeshEditor mesh_editor;
+ mesh_editor.open(*mesh, dolfin::CellType::Type::triangle, 2, 2);
+ mesh_editor.init_vertices(num_vertices);
+ mesh_editor.init_cells(num_cells);
+
+ // Add vertices to mesh
+ unsigned int vertex_index = 0;
+ std::map<Vertex_handle, unsigned int> vertex_map;
+ for (CDT::Finite_vertices_iterator cgal_vertex = cdt.finite_vertices_begin();
+ cgal_vertex != cdt.finite_vertices_end(); ++cgal_vertex)
+ {
+ // Get vertex coordinates and add vertex to the mesh
+ dolfin::Point p;
+ p[0] = cgal_vertex->point()[0];
+ p[1] = cgal_vertex->point()[1];
+
+ // Add mesh vertex
+ mesh_editor.add_vertex(vertex_index, p);
+
+ // Attach index to vertex and increment
+ vertex_map[cgal_vertex] = vertex_index;
+ vertex_index++;
+ }
+
+ dolfin_assert(vertex_index == num_vertices);
+
+ // Add cells to mesh and build domain marker mesh function
+ dolfin::MeshDomains &domain_markers = mesh->domains();
+ std::size_t cell_index = 0;
+ const bool mark_cells = !subdomains.empty();
+ for (CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ // Add cell if it is in the domain
+ if (cgal_cell->is_in_domain())
+ {
+ mesh_editor.add_cell(cell_index,
+ vertex_map[cgal_cell->vertex(0)],
+ vertex_map[cgal_cell->vertex(1)],
+ vertex_map[cgal_cell->vertex(2)]);
+
+ if (mark_cells)
+ {
+ domain_markers.set_marker(std::make_pair(cell_index,
+ subdomain_map[cgal_cell]), 2);
+ }
+ ++cell_index;
+ }
+ }
+ dolfin_assert(cell_index == num_cells);
+
+ // Close mesh editor
+ mesh_editor.close();
+ }
+
+ // Distribute the mesh (if in parallel)
+ if (partition)
+ dolfin::MeshPartitioning::build_distributed_mesh(*mesh);
+
+ return mesh;
+}
+}
+//-----------------------------------------------------------------------------
--- /dev/null
+// Copyright (C) 2012-2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+
+#include <mshr/CSGCGALMeshGenerator3D.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGCGALDomain3D.h>
+
+#include <dolfin/common/MPI.h>
+#include <dolfin/log/LogStream.h>
+#include <dolfin/mesh/BoundaryMesh.h>
+#include <dolfin/mesh/CellType.h>
+#include <dolfin/mesh/MeshEditor.h>
+#include <dolfin/mesh/MeshPartitioning.h>
+
+#include <chrono>
+#include <thread>
+#include <memory>
+
+#define CGAL_NO_DEPRECATED_CODE
+#define CGAL_MESH_3_NO_DEPRECATED_SURFACE_INDEX
+#define CGAL_MESH_3_NO_DEPRECATED_C3T3_ITERATORS
+
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Mesh_triangulation_3.h>
+#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
+#include <CGAL/Mesh_criteria_3.h>
+#include "Polyhedral_multicomponent_mesh_domain_with_features_3.h"
+#include "make_multicomponent_mesh_3.h"
+
+// Bounding sphere computation
+#include <CGAL/Min_sphere_of_spheres_d.h>
+#include <CGAL/Min_sphere_of_spheres_d_traits_3.h>
+
+//#define NO_MULTICOMPONENT_DOMAIN
+
+// Domain
+typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
+typedef CGAL::Mesh_polyhedron_3<K>::type MeshPolyhedron_3;
+typedef K::Point_3 Point_3;
+typedef K::Vector_3 Vector_3;
+typedef K::Triangle_3 Triangle_3;
+
+#ifdef NO_MULTICOMPONENT_DOMAIN
+typedef CGAL::Polyhedral_mesh_domain_with_features_3<K> PolyhedralMeshDomain;
+#else
+typedef Polyhedral_multicomponent_mesh_domain_with_features_3<K> PolyhedralMeshDomain;
+#endif
+
+// Triangulation
+typedef CGAL::Mesh_triangulation_3<PolyhedralMeshDomain>::type Tr;
+// typedef CGAL::Mesh_complex_3_in_triangulation_3<
+// Tr,PolyhedralMeshDomain::Corner_index,PolyhedralMeshDomain::Curve_segment_index> C3t3;
+
+typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3;
+
+// Criteria
+typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;
+
+namespace
+{
+//-----------------------------------------------------------------------------
+// Repeatedly output some data from the triangulation. Return when the mutex is
+// released.
+void output_num_vertices(Tr& triangulation, std::timed_mutex& mutex)
+{
+ std::chrono::seconds timeout(1);
+
+ while (!mutex.try_lock_for(timeout))
+ {
+ // Be carefull to only call functions which are thread safe.
+ // Inspecting the triangulation data structure reveals that both
+ // Tr::number_of_vertices() and Tr::number_of_cells() just reads an
+ // int. Both Tr::number_of_finite_cells and
+ // Tr::number_of_finite_vertices() count by iterating through the data
+ // (which creates a race condition).
+ dolfin::log(dolfin::PROGRESS,
+ "Generating mesh: Currently approx. %ld vertices and %ld cells",
+ triangulation.number_of_vertices(),
+ triangulation.number_of_cells());
+ }
+}
+//-----------------------------------------------------------------------------
+void build_dolfin_mesh(const C3t3& c3t3, dolfin::Mesh& mesh)
+{
+ typedef C3t3::Triangulation Triangulation;
+ typedef Triangulation::Vertex_handle Vertex_handle;
+
+ // Collect the vertices that are part of the complex
+ std::map<Vertex_handle, std::size_t> vertex_id_map;
+ for(C3t3::Cells_in_complex_iterator cit = c3t3.cells_in_complex_begin();
+ cit != c3t3.cells_in_complex_end();
+ ++cit)
+ {
+ for (std::size_t i = 0; i < 4; i++)
+ {
+ if (!vertex_id_map.count(cit->vertex(i)))
+ {
+ const std::size_t vertex_index = vertex_id_map.size();
+ vertex_id_map[cit->vertex(i)] = vertex_index;
+ }
+ }
+ }
+
+ // Create and initialize mesh editor
+
+ // dolfin::Mesh::clear has been removed
+ // mesh.clear();
+
+ dolfin::MeshEditor mesh_editor;
+ mesh_editor.open(mesh, dolfin::CellType::Type::tetrahedron, 3, 3);
+ mesh_editor.init_vertices(vertex_id_map.size());
+ mesh_editor.init_cells(c3t3.number_of_cells_in_complex());
+
+ // Add vertices to mesh
+ for (std::map<Vertex_handle, std::size_t>::const_iterator it = vertex_id_map.cbegin();
+ it != vertex_id_map.cend(); it++)
+ {
+ // Get vertex coordinates and add vertex to the mesh
+ dolfin::Point p(it->first->point()[0], it->first->point()[1], it->first->point()[2]);
+ mesh_editor.add_vertex(it->second, p);
+ }
+
+ // Add cells to mesh
+ std::size_t cell_index = 0;
+ for(C3t3::Cells_in_complex_iterator cit = c3t3.cells_in_complex_begin();
+ cit != c3t3.cells_in_complex_end();
+ ++cit)
+ {
+ mesh_editor.add_cell(cell_index,
+ vertex_id_map[cit->vertex(0)],
+ vertex_id_map[cit->vertex(1)],
+ vertex_id_map[cit->vertex(2)],
+ vertex_id_map[cit->vertex(3)]);
+
+ ++cell_index;
+ }
+
+ // Close mesh editor
+ mesh_editor.close();
+}
+//-----------------------------------------------------------------------------
+struct Copy_polyhedron_to
+ : public CGAL::Modifier_base<typename MeshPolyhedron_3::HalfedgeDS>
+{
+ Copy_polyhedron_to(const mshr::CSGCGALDomain3D& in_poly, bool flip)
+ : _in_poly(in_poly), flip(flip) {}
+
+ void operator()(typename MeshPolyhedron_3::HalfedgeDS& out_hds)
+ {
+ typedef typename MeshPolyhedron_3::HalfedgeDS Output_HDS;
+ CGAL::Polyhedron_incremental_builder_3<Output_HDS> builder(out_hds);
+
+ builder.begin_surface(_in_poly.num_vertices(),
+ _in_poly.num_facets(),
+ _in_poly.num_halfedges());
+
+ {
+ std::unique_ptr<std::vector<double>> v = _in_poly.get_vertices();
+
+ for (std::size_t i = 0; i < v->size(); i += 3)
+ {
+ typename MeshPolyhedron_3::Point_3 p( (*v)[i], (*v)[i+1], (*v)[i+2] );
+ builder.add_vertex(p);
+ }
+ }
+
+ {
+ std::unique_ptr<const std::vector<std::size_t>> f = _in_poly.get_facets();
+
+ for (std::size_t i = 0; i < f->size(); i += 3)
+ {
+ builder.begin_facet();
+ builder.add_vertex_to_facet( (*f)[i] );
+ if (flip)
+ {
+ builder.add_vertex_to_facet( (*f)[i+2] );
+ builder.add_vertex_to_facet( (*f)[i+1] );
+ }
+ else
+ {
+ builder.add_vertex_to_facet( (*f)[i+1] );
+ builder.add_vertex_to_facet( (*f)[i+2] );
+ }
+
+ builder.end_facet();
+ }
+ }
+ builder.end_surface();
+ }
+private:
+ const mshr::CSGCGALDomain3D& _in_poly;
+ const bool flip;
+};
+
+static void convert_to_inexact(const mshr::CSGCGALDomain3D &exact_domain,
+ MeshPolyhedron_3 &inexact_domain,
+ bool flip)
+{
+ Copy_polyhedron_to modifier(exact_domain, flip);
+ inexact_domain.delegate(modifier);
+ CGAL_assertion(inexact_domain.is_valid());
+}
+//-----------------------------------------------------------------------------
+double get_bounding_sphere_radius(const MeshPolyhedron_3& polyhedron)
+{
+ typedef CGAL::Min_sphere_of_spheres_d_traits_3<K, K::FT> MinSphereTraits;
+ typedef CGAL::Min_sphere_of_spheres_d<MinSphereTraits> Min_sphere;
+ typedef MinSphereTraits::Sphere Sphere;
+
+ std::vector<Sphere> S;
+
+ for (MeshPolyhedron_3::Vertex_const_iterator it=polyhedron.vertices_begin();
+ it != polyhedron.vertices_end(); ++it)
+ {
+ S.push_back(Sphere(it->point(), 0.0));
+ }
+
+ Min_sphere ms(S.begin(), S.end());
+ CGAL_assertion(ms.is_valid());
+
+ return CGAL::to_double(ms.radius());
+}
+} //end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+CSGCGALMeshGenerator3D::CSGCGALMeshGenerator3D()
+{
+ parameters = default_parameters();
+}
+//-----------------------------------------------------------------------------
+CSGCGALMeshGenerator3D::~CSGCGALMeshGenerator3D() {}
+//-----------------------------------------------------------------------------
+void CSGCGALMeshGenerator3D::generate(std::shared_ptr<const CSGCGALDomain3D> csgdomain,
+ dolfin::Mesh& mesh) const
+{
+
+ // Note that if not in parallel (ie. size() == 0)
+ // then both receiver and broadcaster will return false
+ if (!dolfin::MPI::is_receiver(mesh.mpi_comm()))
+ {
+ // Create CGAL mesh domain
+ MeshPolyhedron_3 p;
+ convert_to_inexact(*csgdomain, p, !csgdomain->is_insideout());
+
+ // Reset the (memory consuming exact arithmetic) domain object
+ // will be deleted if the pointer has been moved to the function
+ csgdomain.reset();
+
+ // Workaround, cgal segfaulted when assigning new mesh criterias
+ // within the if-else blocks.
+ std::unique_ptr<Mesh_criteria> criteria;
+ double edge_size;
+
+ const bool criteria_changed = parameters["edge_size"].change_count() > 0
+ || parameters["facet_angle"].change_count() > 0
+ || parameters["facet_size"].change_count() > 0
+ || parameters["facet_distance"].change_count() > 0
+ || parameters["cell_radius_edge_ratio"].change_count() > 0
+ || parameters["cell_size"].change_count() > 0;
+
+ if (parameters["mesh_resolution"].change_count() > 0 && criteria_changed)
+ dolfin::warning("Attempt to set both mesh_resolution and other meshing criterias which are mutually exclusive");
+
+ if (criteria_changed)
+ {
+ log(dolfin::TRACE, "Using user specified meshing criterias");
+
+ // Mesh criteria
+ criteria.reset(new Mesh_criteria(CGAL::parameters::edge_size = parameters["edge_size"],
+ CGAL::parameters::facet_angle = parameters["facet_angle"],
+ CGAL::parameters::facet_size = parameters["facet_size"], // <-----------
+ CGAL::parameters::facet_distance = parameters["facet_distance"],
+ CGAL::parameters::cell_radius_edge_ratio = parameters["cell_radius_edge_ratio"],
+ CGAL::parameters::cell_size = parameters["cell_size"])); // <--------------
+ edge_size = parameters["edge_size"];
+ }
+ else
+ {
+ // Try to compute reasonable parameters
+ const double mesh_resolution = parameters["mesh_resolution"];
+ const double r = get_bounding_sphere_radius(p);
+ const double cell_size = r/mesh_resolution*2.0;
+ log(dolfin::TRACE, "Computing meshing criterias. Chose cell size %f", cell_size);
+
+ criteria.reset(new Mesh_criteria(CGAL::parameters::edge_size = cell_size,
+ CGAL::parameters::facet_angle = 30.0,
+ CGAL::parameters::facet_size = cell_size,
+ CGAL::parameters::facet_distance = cell_size/10.0, // ???
+ CGAL::parameters::cell_radius_edge_ratio = 3.0,
+ CGAL::parameters::cell_size = cell_size));
+ edge_size = cell_size;
+ }
+
+ #ifdef NO_MULTICOMPONENT_DOMAIN
+ PolyhedralMeshDomain domain(p);
+ #else
+ PolyhedralMeshDomain domain(p, edge_size);
+ #endif
+
+ if (parameters["detect_sharp_features"])
+ {
+ log(dolfin::TRACE, "Detecting sharp features");
+
+ const double feature_threshold = parameters["feature_threshold"];
+ domain.detect_features(feature_threshold);
+ }
+
+ // Mesh generation
+ log(dolfin::TRACE, "Generating mesh");
+ C3t3 c3t3;
+ {
+ std::timed_mutex mutex;
+ mutex.lock();
+ std::thread output_thread(output_num_vertices,
+ std::ref(c3t3.triangulation()), std::ref(mutex));
+ dolfin::begin("Generating mesh with CGAL 3D mesh generator");
+ make_multicomponent_mesh_3_impl<C3t3>(c3t3,
+ domain,
+ *criteria,
+ CGAL::parameters::no_exude(),
+ CGAL::parameters::no_perturb(),
+ CGAL::parameters::no_odt(),
+ CGAL::parameters::no_lloyd(),
+ true);
+ mutex.unlock();
+ output_thread.join();
+ dolfin::end();
+ }
+
+ log(dolfin::TRACE, "Done");
+ if (parameters["odt_optimize"])
+ {
+ log(dolfin::TRACE, "Optimizing mesh by odt optimization");
+ odt_optimize_mesh_3(c3t3, domain);
+ }
+
+ if (parameters["lloyd_optimize"])
+ {
+ log(dolfin::TRACE, "Optimizing mesh by lloyd optimization");
+ lloyd_optimize_mesh_3(c3t3, domain);
+ }
+
+ if (parameters["perturb_optimize"])
+ {
+ log(dolfin::TRACE, "Optimizing mesh by perturbation");
+ // TODO: Set time limit
+ CGAL::perturb_mesh_3(c3t3, domain);
+ }
+
+ if (parameters["exude_optimize"])
+ {
+ log(dolfin::TRACE, "Optimizing mesh by sliver exudation");
+ exude_mesh_3(c3t3);
+ }
+
+ build_dolfin_mesh(c3t3, mesh);
+ }
+
+ // Distribute the mesh (if in parallel)
+ dolfin::MeshPartitioning::build_distributed_mesh(mesh);
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh>
+CSGCGALMeshGenerator3D::generate(std::shared_ptr<const CSGCGALDomain3D> csgdomain) const
+{
+ std::shared_ptr<dolfin::Mesh> mesh(new dolfin::Mesh());
+ generate(csgdomain, *mesh);
+ return mesh;
+}
+}
--- /dev/null
+// Copyright (C) 2012-2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring 2014
+// Modified by Anders Logg 2014
+
+#include <mshr/CSGGeometries3D.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGPrimitives3D.h>
+#include <mshr/CSGOperators.h>
+
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/mesh/BoundaryMesh.h>
+#include <dolfin/mesh/Edge.h>
+
+#include <algorithm>
+#include <cmath>
+
+#include "Polygon_utils.h"
+
+namespace mshr
+{
+
+// FIXME: Move somewhere else
+// Convenience function for rotation
+std::shared_ptr<CSGGeometry> rotate(std::shared_ptr<CSGGeometry> geometry,
+ dolfin::Point rot_axis,
+ dolfin::Point rot_center,
+ double theta)
+{
+ return std::shared_ptr<CSGGeometry>(new CSGRotation(geometry,
+ rot_axis,
+ rot_center,
+ theta));
+}
+
+//-----------------------------------------------------------------------------
+std::shared_ptr<CSGGeometry> CSGGeometries::lego(std::size_t n0,
+ std::size_t n1,
+ std::size_t n2,
+ dolfin::Point x)
+{
+ // Standard dimensions for LEGO bricks / m
+ const double P = 8.0 * 0.001;
+ const double h = 3.2 * 0.001;
+ const double D = 5.0 * 0.001;
+ const double b = 1.7 * 0.001;
+ const double d = 0.2 * 0.001;
+
+ // Create brick
+ std::shared_ptr<CSGGeometry>
+ lego(new Box(x + dolfin::Point(0.5*d, 0.5*d, 0),
+ x + dolfin::Point(n0*P - 0.5*d, n1*P - 0.5*d, n2*h)));
+
+ // Add knobs
+ for (std::size_t i = 0; i < n0; i++)
+ {
+ for (std::size_t j = 0; j < n1; j++)
+ {
+ const dolfin::Point knop_bottom = x + dolfin::Point( (i + 0.5)*P,
+ (j + 0.5)*P,
+ 0);
+
+ std::shared_ptr<CSGGeometry>
+ knob(new Cylinder(knop_bottom,
+ knop_bottom + dolfin::Point(0, 0, n2*h + b),
+ 0.5*D,
+ 0.5*D));
+
+ lego = lego + knob;
+ }
+ }
+
+ return lego;
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<CSGGeometry> CSGGeometries::propeller(double r,
+ double R,
+ double w,
+ double h,
+ bool rotate_blades,
+ bool include_tip)
+{
+ // Parameters
+ const double v = 0.3; // rotation of blades
+ const double l = 0.8*w; // length of cone
+
+ // // Create blades
+ std::shared_ptr<CSGGeometry>
+ blade_0(new Box(dolfin::Point(0.8*r, -0.5*h, -0.5*w),
+ dolfin::Point( R, 0.5*h, 0.5*w)));
+ std::shared_ptr<CSGGeometry>
+ blade_1(new Box(dolfin::Point( -R, -0.5*h, -0.5*w),
+ dolfin::Point(-0.8*r, 0.5*h, 0.5*w)));
+ std::shared_ptr<CSGGeometry>
+ blade_2(new Box(dolfin::Point(-0.5*h, 0.8*r, -0.5*w),
+ dolfin::Point( 0.5*h, R, 0.5*w)));
+ std::shared_ptr<CSGGeometry>
+ blade_3(new Box(dolfin::Point(-0.5*h, -R, -0.5*w),
+ dolfin::Point( 0.5*h, -0.8*r, 0.5*w)));
+
+ // Rotate blades
+ if (rotate_blades)
+ {
+ blade_0 = rotate(blade_0, dolfin::Point(1, 0, 0), dolfin::Point(0, 0, 0), v);
+ blade_1 = rotate(blade_1, dolfin::Point(1, 0, 0), dolfin::Point(0, 0, 0), -v);
+ blade_2 = rotate(blade_2, dolfin::Point(0, 1, 0), dolfin::Point(0, 0, 0), v);
+ blade_3 = rotate(blade_3, dolfin::Point(0, 1, 0), dolfin::Point(0, 0, 0), -v);
+ }
+
+ // Create blade tips
+ std::shared_ptr<CSGGeometry>
+ blade_tip_0(new Cylinder(dolfin::Point( R, -0.5*h, 0),
+ dolfin::Point( R, 0.5*h, 0), 0.5*w, 0.5*w));
+ std::shared_ptr<CSGGeometry>
+ blade_tip_1(new Cylinder(dolfin::Point(-R, -0.5*h, 0),
+ dolfin::Point(-R, 0.5*h, 0), 0.5*w, 0.5*w));
+ std::shared_ptr<CSGGeometry>
+ blade_tip_2(new Cylinder(dolfin::Point(-0.5*h, R, 0),
+ dolfin::Point( 0.5*h, R, 0), 0.5*w, 0.5*w));
+ std::shared_ptr<CSGGeometry>
+ blade_tip_3(new Cylinder(dolfin::Point(-0.5*h, -R, 0),
+ dolfin::Point( 0.5*h, -R, 0), 0.5*w, 0.5*w));
+
+ // Rotate blades
+ if (rotate_blades)
+ {
+ blade_tip_0 = rotate(blade_tip_0, dolfin::Point(1, 0, 0), dolfin::Point(0, 0, 0), v);
+ blade_tip_1 = rotate(blade_tip_1, dolfin::Point(1, 0, 0), dolfin::Point(0, 0, 0), -v);
+ blade_tip_2 = rotate(blade_tip_2, dolfin::Point(0, 1, 0), dolfin::Point(0, 0, 0), v);
+ blade_tip_3 = rotate(blade_tip_3, dolfin::Point(0, 1, 0), dolfin::Point(0, 0, 0), -v);
+ }
+
+ // Add blade tips
+ blade_0 = blade_0 + blade_tip_0;
+ blade_1 = blade_1 + blade_tip_1;
+ blade_2 = blade_2 + blade_tip_2;
+ blade_3 = blade_3 + blade_tip_3;
+
+ // // Add blades
+ std::shared_ptr<CSGGeometry>
+ blades = blade_0 + blade_1 + blade_2 + blade_3;
+
+ // Create outer cylinder
+ std::shared_ptr<CSGGeometry>
+ cylinder_outer(new Cylinder(dolfin::Point(0, 0, -0.5*w),
+ dolfin::Point(0, 0, 0.5*w),
+ r, r));
+
+ // Create inner cylinder
+ std::shared_ptr<CSGGeometry>
+ cylinder_inner(new Cylinder(dolfin::Point(0, 0, -0.5*w),
+ dolfin::Point(0, 0, 0.5*w),
+ 0.5*r, 0.5*r));
+
+ // Create center cone
+ std::shared_ptr<CSGGeometry>
+ cone(new Cylinder(dolfin::Point(0, 0, -0.5*w),
+ dolfin::Point(0, 0, -0.5*w - l), r, h));
+
+ // Create sphere for tip of cone
+ const double k = (r - h) / l;
+ const double rc = h*sqrt(1 + k*k);
+ const double xc = k*h;
+ std::shared_ptr<CSGGeometry>
+ tip(new Sphere(dolfin::Point(0, 0, -0.5*w - l + xc), rc));
+
+ // Build propeller from parts
+ if (include_tip)
+ return cylinder_outer - cylinder_inner + blades + cone + tip;
+ else
+ return cylinder_outer - cylinder_inner + blades + cone;
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<CSGGeometry> CSGGeometries::import_mesh(std::shared_ptr<dolfin::Mesh> mesh)
+{
+ std::shared_ptr<CSGGeometry> g;
+ if (mesh->geometry().dim() == 2)
+ {
+ // Import and right hand orient boundary entities (not UFC orientation)
+ dolfin::BoundaryMesh bnd(*mesh, "exterior", false);
+ const std::vector<double>& coordinates = bnd.coordinates();
+
+ std::vector<std::vector<dolfin::Point>> polygons;
+ std::vector<std::vector<dolfin::Point>> holes;
+
+ std::map<std::size_t, std::size_t> edges;
+ for (dolfin::EdgeIterator e(bnd); !e.end(); ++e)
+ {
+ const unsigned int* vertices = e->entities(0);
+ edges.emplace(std::make_pair(vertices[0], vertices[1]));
+ }
+
+ while (edges.size() > 0)
+ {
+ std::vector<dolfin::Point> polygon_vertices;
+ std::map<std::size_t, std::size_t>::iterator current = edges.begin();
+ std::size_t start = current->first;
+ std::size_t next;
+ do
+ {
+ polygon_vertices.push_back(dolfin::Point(coordinates[2*current->second], coordinates[2*current->second+1]));
+ next = current->second;
+ edges.erase(current);
+ current = edges.find(next);
+ } while(next != start);
+
+ if (PolygonUtils::ccw(polygon_vertices))
+ polygons.push_back(polygon_vertices);
+ else
+ {
+ std::reverse(polygon_vertices.begin(), polygon_vertices.end());
+ holes.push_back(polygon_vertices);
+ }
+ }
+
+ {
+ std::vector<std::vector<dolfin::Point>>::iterator it = polygons.begin();
+ g = std::make_shared<Polygon>(*it);
+ it++;
+
+ for(; it != polygons.end(); it++)
+ {
+ std::shared_ptr<CSGGeometry> h = std::make_shared<Polygon>(*it);
+ g = std::make_shared<CSGUnion>(g, h);
+ }
+ }
+
+ for (std::vector<std::vector<dolfin::Point>>::iterator it = holes.begin();
+ it != holes.end();
+ it++)
+ {
+ std::shared_ptr<CSGGeometry> h = std::make_shared<Polygon>(*it);
+ g = std::make_shared<CSGDifference>(g, h);
+ }
+ }
+ else
+ {
+ dolfin::dolfin_error("CSGGeometries.cpp",
+ "importing 3D mesh",
+ "use mshr::Surface3D for now");
+ }
+
+ return g;
+}
+
+
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg, Benjamin Kehlet 2013-2017
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <dolfin/common/NoDeleter.h>
+#include <dolfin/log/LogStream.h>
+#include <mshr/CSGGeometry.h>
+
+// Bounding sphere computation
+#include <CGAL/Cartesian.h>
+#include <CGAL/Min_sphere_of_spheres_d.h>
+#include <CGAL/Min_sphere_of_spheres_d_traits_3.h>
+
+namespace mshr
+{
+
+//-----------------------------------------------------------------------------
+CSGGeometry::CSGGeometry()
+{
+ // Do nothing
+}
+//-----------------------------------------------------------------------------
+CSGGeometry::~CSGGeometry()
+{
+ // Do nothing
+}
+//-----------------------------------------------------------------------------
+void CSGGeometry::set_subdomain(std::size_t i, std::shared_ptr<CSGGeometry> s)
+{
+ if (dim() != 2)
+ dolfin::dolfin_error("CSGGeometry.cpp",
+ "setting subdomain",
+ "Subdomains are currently supported only in 2D");
+
+ if (s->dim() != dim())
+ dolfin::dolfin_error("CSGGeometry.cpp",
+ "setting subdomain",
+ "Subdomain and domain must be of same dimension. Domain was dimension %d and subdomain was %d", dim(), s->dim());
+
+ if (i == 0)
+ {
+ dolfin::dolfin_error("CSGGeometry.cpp",
+ "Setting reserved CSG subdomain (0)",
+ " Subdomain 0 is reserved and cannot be set by user");
+ }
+
+ // Check if i already used
+ std::list<std::pair<std::size_t, std::shared_ptr<const CSGGeometry> > >::iterator it = subdomains.begin();
+ while (it != subdomains.end())
+ {
+ if (it->first == i)
+ {
+ dolfin::warning("Double declaration of CSG subdomain with index %u.", i);
+
+ // Remove existing declaration
+ it = subdomains.erase(it);
+ }
+ else
+ ++it;
+ }
+
+ subdomains.push_back(std::make_pair(i, s));
+}
+//-----------------------------------------------------------------------------
+void CSGGeometry::set_subdomain(std::size_t i, CSGGeometry& s)
+{
+ set_subdomain(i, reference_to_no_delete_pointer(s));
+}
+//-----------------------------------------------------------------------------
+bool CSGGeometry::has_subdomains() const
+{
+ return subdomains.size() > 0;
+}
+//-----------------------------------------------------------------------------
+bool CSGGeometry::inside(dolfin::Point p1, dolfin::Point p2) const
+{
+ dolfin_not_implemented();
+ return false;
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, double> CSGGeometry::estimate_bounding_sphere(std::size_t numSamples) const
+{
+ std::pair<dolfin::Point, dolfin::Point> aabb = bounding_box();
+ const dolfin::Point min = aabb.first;
+ const dolfin::Point max = aabb.second;
+
+ //typedef CGAL::Exact_predicates_exact_constructions_kernel K;
+ typedef CGAL::Cartesian<double> K;
+ typedef K::Point_2 Point_2;
+ typedef CGAL::Min_sphere_of_spheres_d_traits_2<K, K::FT> MinSphereTraits;
+ typedef CGAL::Min_sphere_of_spheres_d<MinSphereTraits> Min_sphere;
+ typedef MinSphereTraits::Sphere Sphere;
+
+ std::vector<Sphere> S;
+ S.reserve(numSamples);
+ const std::size_t N = static_cast<std::size_t>(std::sqrt(static_cast<double>(numSamples)+.5));
+ const double dX = (max.x()-min.x())/N;
+ const double dY = (max.y()-min.y())/N;
+ // const double dTheta = 2*DOLFIN_PI/N;
+ for (std::size_t i = 0; i < N; i++)
+ {
+ for (std::size_t j = 0; j < N; j++)
+ {
+ const dolfin::Point p(min.x() + i*dX, min.y() + j*dY);
+ if (inside(p))
+ S.push_back(Sphere(Point_2(p.x(), p.y()), 0.0));
+ }
+ }
+
+ Min_sphere ms(S.begin(), S.end());
+ CGAL_assertion(ms.is_valid());
+
+ auto it = ms.center_cartesian_begin();
+ const double center_x = CGAL::to_double(*it);
+ it++;
+ const double center_y = CGAL::to_double(*it);
+ const double radius = CGAL::to_double(ms.radius());
+
+ return std::make_pair(dolfin::Point(center_x, center_y),
+ radius);
+}
+
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg, Benjamin Kehlet 2013-2017
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+#include <mshr/CSGOperators.h>
+
+#include <dolfin/common/utils.h>
+#include <dolfin/log/log.h>
+#include <dolfin/common/constants.h>
+
+#include <sstream>
+
+namespace mshr
+{
+
+//-----------------------------------------------------------------------------
+// CSGUnion
+//-----------------------------------------------------------------------------
+CSGOperator::CSGOperator()
+{}
+
+//-----------------------------------------------------------------------------
+// CSGUnion
+//-----------------------------------------------------------------------------
+CSGUnion::CSGUnion(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ : _g0(g0), _g1(g1)
+{
+ assert(g0);
+ assert(g1);
+
+ // Check dimensions
+ if (g0->dim() != g1->dim())
+ {
+ dolfin::dolfin_error("CSGOperators.cpp",
+ "create union of CSG geometries",
+ "Dimensions of geometries don't match (%d vs %d)",
+ g0->dim(), g1->dim());
+ }
+
+ dim_ = g0->dim();
+}
+//-----------------------------------------------------------------------------
+std::string CSGUnion::str(bool verbose) const
+{
+ assert(_g0);
+ assert(_g1);
+
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Union>\n"
+ << "{\n"
+ << dolfin::indent(_g0->str(true))
+ << "\n"
+ << dolfin::indent(_g1->str(true))
+ << "\n}";
+ }
+ else
+ {
+ s << "(" << _g0->str(false) << " + " << _g1->str(false) << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGUnion::bounding_box() const
+{
+ std::pair<dolfin::Point, dolfin::Point> a = _g0->bounding_box();
+ std::pair<dolfin::Point, dolfin::Point> b = _g1->bounding_box();
+
+ return std::make_pair(dolfin::Point(std::min(a.first.x(), b.first.x()),
+ std::min(a.first.y(), b.first.y()),
+ std::min(a.first.z(), b.first.z())),
+ dolfin::Point(std::max(a.second.x(), b.second.x()),
+ std::max(a.second.y(), b.second.y()),
+ std::max(a.second.z(), b.second.z())));
+}
+//-----------------------------------------------------------------------------
+bool CSGUnion::inside(dolfin::Point p) const
+{
+ return _g0->inside(p) || _g1->inside(p);
+}
+
+//-----------------------------------------------------------------------------
+// CSGDifference
+//-----------------------------------------------------------------------------
+CSGDifference::CSGDifference(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ : _g0(g0), _g1(g1)
+{
+ assert(g0);
+ assert(g1);
+
+ // Check dimensions
+ if (g0->dim() != g1->dim())
+ {
+ dolfin::dolfin_error("CSGOperators.cpp",
+ "create difference of CSG geometries",
+ "Dimensions of geomestries don't match (%d vs %d)",
+ g0->dim(), g1->dim());
+ }
+
+ dim_ = g0->dim();
+}
+//-----------------------------------------------------------------------------
+std::string CSGDifference::str(bool verbose) const
+{
+ assert(_g0);
+ assert(_g1);
+
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Difference>\n"
+ << "{\n"
+ << dolfin::indent(_g0->str(true))
+ << "\n"
+ << dolfin::indent(_g1->str(true))
+ << "\n}";
+ }
+ else
+ {
+ s << "(" << _g0->str(false) << " - " << _g1->str(false) << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGDifference::bounding_box() const
+{
+ // FIXME: This is where the bounding_box implementation may overshoot
+ return _g0->bounding_box();
+}
+//-----------------------------------------------------------------------------
+bool CSGDifference::inside(dolfin::Point p) const
+{
+ return _g0->inside(p) && !_g1->inside(p);
+}
+
+//-----------------------------------------------------------------------------
+// CSGIntersection
+//-----------------------------------------------------------------------------
+CSGIntersection::CSGIntersection(std::shared_ptr<CSGGeometry> g0,
+ std::shared_ptr<CSGGeometry> g1)
+ : _g0(g0), _g1(g1)
+{
+ assert(g0);
+ assert(g1);
+
+ // Check dimensions
+ if (g0->dim() != g1->dim())
+ {
+ dolfin::dolfin_error("CSGOperators.cpp",
+ "create intersection of CSG geometries",
+ "Dimensions of geomestries don't match (%d vs %d)",
+ g0->dim(), g1->dim());
+ }
+
+ dim_ = g0->dim();
+}
+//-----------------------------------------------------------------------------
+std::string CSGIntersection::str(bool verbose) const
+{
+ assert(_g0);
+ assert(_g1);
+
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Intersection>\n"
+ << "{\n"
+ << dolfin::indent(_g0->str(true))
+ << "\n"
+ << dolfin::indent(_g1->str(true))
+ << "\n}";
+ }
+ else
+ {
+ s << "(" << _g0->str(false) << " * " << _g1->str(false) << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGIntersection::bounding_box() const
+{
+ std::pair<dolfin::Point, dolfin::Point> a = _g0->bounding_box();
+ std::pair<dolfin::Point, dolfin::Point> b = _g1->bounding_box();
+
+ return std::make_pair(dolfin::Point(std::min(a.first.x(), b.first.x()),
+ std::min(a.first.y(), b.first.y()),
+ std::min(a.first.z(), b.first.z())),
+ dolfin::Point(std::max(a.second.x(), b.second.x()),
+ std::max(a.second.y(), b.second.y()),
+ std::max(a.second.z(), b.second.z())));
+}
+//-----------------------------------------------------------------------------
+bool CSGIntersection::inside(dolfin::Point p) const
+{
+ return _g0->inside(p) && _g1->inside(p);
+}
+
+//-----------------------------------------------------------------------------
+// CSGTranslation
+//-----------------------------------------------------------------------------
+CSGTranslation::CSGTranslation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point t)
+ : g(g), t(t)
+{
+ assert(g);
+
+ dim_ = g->dim();
+}
+//-----------------------------------------------------------------------------
+std::string CSGTranslation::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Translation>\n"
+ << "{\n"
+ << dolfin::indent(g->str(true) + "\nby\n" + t.str(true))
+ << "\n}";
+ }
+ else
+ {
+ s << "(" << g->str(false) << " + " << t.str(false) << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGTranslation::bounding_box() const
+{
+ std::pair<dolfin::Point, dolfin::Point> aabb = g->bounding_box();
+
+ return std::make_pair(aabb.first+t, aabb.second+t);
+}
+//-----------------------------------------------------------------------------
+bool CSGTranslation::inside(dolfin::Point p) const
+{
+ return g->inside(p-t);
+}
+
+//-----------------------------------------------------------------------------
+// CSGScaling
+//-----------------------------------------------------------------------------
+CSGScaling::CSGScaling(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point c,
+ double s)
+ : g(g), c(c), s(s), translate(true)
+{
+ assert(g);
+
+ dim_ = g->dim();
+}
+//-----------------------------------------------------------------------------
+CSGScaling::CSGScaling(std::shared_ptr<CSGGeometry> g,
+ double s)
+ : g(g), c(0,0,0), s(s), translate(false)
+{
+ assert(g);
+
+ dim_ = g->dim();
+}
+//-----------------------------------------------------------------------------
+std::string CSGScaling::str(bool verbose) const
+{
+ std::stringstream ss;
+
+ if (verbose)
+ {
+ ss << "<Scaling>\n"
+ << "{\n"
+ << dolfin::indent(g->str(true) + "\nby\n" + std::to_string(s));
+
+ if (translate)
+ ss << "\naround " << c.str(true);
+
+ ss << "\n}";
+ }
+ else
+ {
+ ss << "(" << g->str(false) << " * " << std::to_string(s);
+ if (translate)
+ ss << "(" << c.str(true) << ")";
+ ss << ")";
+ }
+
+ return ss.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGScaling::bounding_box() const
+{
+ std::pair<dolfin::Point, dolfin::Point> aabb = g->bounding_box();
+
+ const dolfin::Point scaled_c = (translate ? (1-s)*c : dolfin::Point(0,0,0));
+
+ if (translate)
+ return std::make_pair((aabb.first-c)*s + c, (aabb.second-c)*s + c);
+ else
+ return std::make_pair(aabb.first*s, aabb.second*s);
+
+}
+//-----------------------------------------------------------------------------
+bool CSGScaling::inside(dolfin::Point p) const
+{
+ const dolfin::Point p_scaled = (translate ? (p-c)*s + c : p*s);
+ return g->inside(p_scaled);
+}
+
+//-----------------------------------------------------------------------------
+// CSGRotation
+//-----------------------------------------------------------------------------
+CSGRotation::CSGRotation(std::shared_ptr<CSGGeometry> g,
+ double theta)
+ : g(g), rot_axis(.0,.0), c(.0,.0), theta(theta), translate(false)
+{
+
+ dim_ = g->dim();
+
+ if (dim_ > 2)
+ dolfin::dolfin_error("CSGOperators.cpp",
+ "Constructing CSG rotation",
+ "Rotation axis must be given in 3D");
+}
+//-----------------------------------------------------------------------------
+CSGRotation::CSGRotation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point v,
+ double theta)
+ : g(g),
+ rot_axis(v),
+ c(v),
+ theta(theta),
+ translate(g->dim() == 2 ? true : false)
+{
+ assert(g);
+
+ dim_ = g->dim();
+
+ // if (dim_ == 2)
+ // translate = true;
+ // else
+ // translate = false;
+}
+//-----------------------------------------------------------------------------
+CSGRotation::CSGRotation(std::shared_ptr<CSGGeometry> g,
+ dolfin::Point rot_axis,
+ dolfin::Point rot_center,
+ double theta)
+ : g(g),
+ rot_axis(rot_axis),
+ c(rot_center),
+ theta(theta),
+ translate(true)
+{
+ assert(g);
+
+ dim_ = g->dim();
+
+ if (dim_ < 3)
+ dolfin::dolfin_error("CSGOperators.cpp",
+ "Constructing CSG rotation",
+ "Can't give rotation axis for dimension < 3");
+}
+//-----------------------------------------------------------------------------
+std::string CSGRotation::str(bool verbose) const
+{
+ std::stringstream ss;
+
+ if (verbose)
+ {
+ ss << "<Rotation>\n"
+ << "{\n"
+ << dolfin::indent(g->str(true)
+ + (translate ? "\naround "+rot_axis.str(true) : "")
+ + "\nby " + std::to_string(theta/DOLFIN_PI) + " PI");
+
+ ss << "\n}";
+ }
+ else
+ {
+ ss << "rotate(" << g->str(false)
+ << ", " << std::to_string(theta/DOLFIN_PI) << " PI";
+
+ if (translate)
+ ss << ", " << rot_axis.str(true);
+
+ ss << ")";
+ }
+
+ return ss.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGRotation::bounding_box() const
+{
+ if (dim() != 2)
+ {
+ dolfin_not_implemented();
+ }
+
+ const std::pair<dolfin::Point, dolfin::Point> aabb = g->bounding_box();
+ const dolfin::Point axis(0,0,1);
+
+ // Rotate all corners
+ const dolfin::Point a = aabb.first.rotate(axis, theta);
+ const dolfin::Point b = dolfin::Point(aabb.first.x(), aabb.second.y()).rotate(axis, theta);
+ const dolfin::Point c = dolfin::Point(aabb.second.x(), aabb.first.y()).rotate(axis, theta);
+ const dolfin::Point d = aabb.second.rotate(axis, theta);
+
+ return std::make_pair(dolfin::Point(std::min(a.x(), std::min(b.x(), std::min(c.x(), d.x()))),
+ std::min(a.y(), std::min(b.y(), std::min(c.y(), d.y())))),
+ dolfin::Point(std::max(a.x(), std::max(b.x(), std::max(c.x(), d.x()))),
+ std::max(a.y(), std::max(b.y(), std::max(c.y(), d.y())))));
+
+}
+//-----------------------------------------------------------------------------
+bool CSGRotation::inside(dolfin::Point p) const
+{
+ const dolfin::Point p_rotated = translate ? (p-c).rotate(dolfin::Point(0,0,1), -theta) + c : p.rotate(dolfin::Point(0,0,1), -theta);
+ return g->inside(p_rotated);
+}
+
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg, 2014-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Johannes Ring, 2012
+
+#include <mshr/CSGPrimitives2D.h>
+#include <dolfin/math/basic.h>
+#include <dolfin/log/LogStream.h>
+
+#include <sstream>
+#include <limits>
+#include <algorithm>
+
+#include <CGAL/Cartesian.h>
+#include <CGAL/Polygon_2.h>
+
+namespace mshr
+{
+//-----------------------------------------------------------------------------
+CSGPrimitive2D::CSGPrimitive2D()
+{}
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Circle
+//-----------------------------------------------------------------------------
+Circle::Circle(dolfin::Point(c), double r, std::size_t segments)
+ : c(c), _r(r), _segments(segments)
+{
+ if (_r < DOLFIN_EPS)
+ {
+ std::stringstream s;
+ s << "Circle with center " << c.str() << " has zero or negative radius";
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create circle",
+ s.str());
+ }
+
+ if (0 < _segments && _segments < 3)
+ {
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create circle",
+ "Unable to create circle with fewer than 3 segments");
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Circle::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Circle at (" << c.str() << ") with radius " << _r << ">";
+ }
+ else
+ {
+ s << "Circle(" << c.str() << ", " << _r << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> Circle::bounding_box() const
+{
+ return std::make_pair(dolfin::Point(c.x()-radius(), c.y()-radius()),
+ dolfin::Point(c.x()+radius(), c.y()+radius()));
+}
+//-----------------------------------------------------------------------------
+bool Circle::inside(dolfin::Point p) const
+{
+ // dolfin::cout << "Inside: " << c << " r=" << _r << " ::: " << p << "(" << ((p-c).squared_norm() <= _r*_r ? "Inside" : "Outside") << dolfin::endl;
+ return (p-c).squared_norm() <= _r*_r;
+}
+
+
+//-----------------------------------------------------------------------------
+// Ellipse
+//-----------------------------------------------------------------------------
+Ellipse::Ellipse(dolfin::Point c, double a, double b,
+ std::size_t segments)
+ : c(c), _a(a), _b(b), _segments(segments)
+{
+ if (_a < DOLFIN_EPS || _b < DOLFIN_EPS)
+ {
+ std::stringstream s;
+ s << "Ellipse with center " << c.str() << " has invalid semi-axis";
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create ellipse",
+ s.str());
+ }
+
+ if (0 < _segments && _segments < 3)
+ {
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create ellipse",
+ "Unable to create ellipse with fewer than 3 segments");
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Ellipse::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Ellipse centered at (" << c.str() << ") with horizontal semi-axis "
+ << _a << " and vertical semi-axis " << _b << ">";
+ }
+ else
+ {
+ s << "Ellipse(" << c.str() << ", " << _a << ", " << _b << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> Ellipse::bounding_box() const
+{
+ return std::make_pair(dolfin::Point(c.x()-a(), c.y()-b()),
+ dolfin::Point(c.x()+a(), c.y()+b()));
+}
+//-----------------------------------------------------------------------------
+bool Ellipse::inside(dolfin::Point p) const
+{
+ const double x = (c.x()-p.x())/_a;
+ const double y = (c.y()-p.y())/_b;
+ return x*x + y*y <= 1;
+}
+
+//-----------------------------------------------------------------------------
+// Rectangle
+//-----------------------------------------------------------------------------
+Rectangle::Rectangle(dolfin::Point a_, dolfin::Point b_)
+ : a(dolfin::Point(std::min(a_.x(), b_.x()), std::min(a_.y(), b_.y()))),
+ b(dolfin::Point(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y())))
+{
+ if (dolfin::near(a.x(), b.x()) || dolfin::near( a.y(), b.y()))
+ {
+ std::stringstream s;
+ s << "Rectangle with corner " << a.str() << " and " << b.str() << " degenerated";
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create rectangle",
+ s.str());
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Rectangle::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Rectangle with first corner at (" << a.str() << ") "
+ << "and second corner at (" << b.str() << ")>";
+ }
+ else
+ {
+ s << "Rectangle( (" << a.str() << "), (" << b.str() << ") )";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> Rectangle::bounding_box() const
+{
+ return std::make_pair(a, b);
+}
+//-----------------------------------------------------------------------------
+bool Rectangle::inside(dolfin::Point p) const
+{
+ return p.x() >= a.x() && p.x() <= b.x() && p.y() >= a.y() && p.y() <= b.y();
+}
+
+
+//-----------------------------------------------------------------------------
+// Polygon
+//-----------------------------------------------------------------------------
+Polygon::Polygon(const std::vector<dolfin::Point>& vertices)
+ : _vertices(vertices.begin(), vertices.end())
+{
+ if (_vertices.size() < 3)
+ {
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create polygon",
+ "Polygon should have at least three vertices");
+ }
+
+ if (!ccw())
+ dolfin::dolfin_error("CSGPrimitives2D.cpp",
+ "create polygon",
+ "Polygon vertices must be given in counter clockwise order");
+}
+//-----------------------------------------------------------------------------
+std::string Polygon::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Polygon with vertices ";
+ std::vector<dolfin::Point>::const_iterator p;
+ for (p = _vertices.begin(); p != _vertices.end(); ++p)
+ {
+ s << "(" << p->x() << ", " << p->y() << ")";
+ if ((p != _vertices.end()) && (p + 1 != _vertices.end()))
+ s << ", ";
+ }
+ s << ">";
+ }
+ else
+ {
+ s << "Polygon (" << _vertices.size() << " vertices)";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+bool Polygon::ccw() const
+{
+ double signed_area = 0.0;
+
+ dolfin::Point prev = _vertices.back();
+ for (std::vector<dolfin::Point>::const_iterator it = _vertices.begin(),
+ v_end = _vertices.end();
+ it != v_end;
+ ++it)
+ {
+ signed_area += (prev.x()*it->y())-(it->x()*prev.y());
+ prev = *it;
+ }
+
+ return signed_area > 0;
+}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> Polygon::bounding_box() const
+{
+ std::vector<dolfin::Point>::const_iterator it = _vertices.begin();
+ dolfin::Point min = *it;
+ dolfin::Point max = *it;
+ it++;
+
+ for (; it != _vertices.end(); it++)
+ {
+ const dolfin::Point& p = *it;
+ min[0] = std::min(min[0], p[0]);
+ min[1] = std::min(min[1], p[1]);
+ min[2] = std::min(min[2], p[2]);
+
+ max[0] = std::max(max[0], p[0]);
+ max[1] = std::max(max[1], p[1]);
+ max[2] = std::max(max[2], p[2]);
+ }
+
+ return std::make_pair(min, max);
+}
+//-----------------------------------------------------------------------------
+bool Polygon::inside(dolfin::Point p) const
+{
+ typedef CGAL::Cartesian<double> K;
+ typedef CGAL::Point_2<K> Point_2;
+ typedef CGAL::Polygon_2<K> Polygon_2;
+
+ Polygon_2 polygon;
+ for (const dolfin::Point& vertex : _vertices)
+ polygon.push_back(Point_2(vertex.x(), vertex.y()));
+
+ return polygon.has_on_bounded_side(Point_2(p.x(), p.y()));
+}
+
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg and 2012, 2014-2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/CSGPrimitives3D.h>
+
+#include <dolfin/math/basic.h>
+#include <dolfin/log/LogStream.h>
+#include <sstream>
+
+namespace mshr
+{
+//-----------------------------------------------------------------------------
+CSGPrimitive3D::CSGPrimitive3D()
+{}
+//-----------------------------------------------------------------------------
+std::pair<dolfin::Point, dolfin::Point> CSGPrimitive3D::bounding_box() const
+{
+ dolfin_not_implemented();
+ return std::make_pair(dolfin::Point(0., 0., 0.), dolfin::Point(0., 0., 0.));
+}
+//-----------------------------------------------------------------------------
+bool CSGPrimitive3D::inside(dolfin::Point p) const
+{
+ dolfin_not_implemented();
+ return false;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Sphere
+//-----------------------------------------------------------------------------
+Sphere::Sphere(dolfin::Point center, double radius, std::size_t segments)
+ : c(center), r(radius), _segments(segments)
+{
+ if (r < DOLFIN_EPS)
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create sphere",
+ "Sphere with center (%f, %f, %f) has zero or negative radius", c.x(), c.y(), c.z());
+ }
+
+ if (segments < 1)
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create sphere",
+ "Can't create sphere with zero segments");
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Sphere::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Sphere with center at " << c << " "
+ << "and radius " << r << ">";
+ }
+ else
+ s << "Sphere(" << c << ", " << r << ")";
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+// Box
+//-----------------------------------------------------------------------------
+Box::Box(dolfin::Point a_, dolfin::Point b_)
+ : a(dolfin::Point(std::min(a_.x(), b_.x()), std::min(a_.y(), b_.y()), std::min(a_.z(), b_.z()))),
+ b(dolfin::Point(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()), std::max(a_.z(), b_.z())))
+{
+ if (dolfin::near(a.x(), b.x()) || dolfin::near(a.y(), b.y()) || dolfin::near(a.z(), b.z()))
+ {
+ std::stringstream s;
+ s << "Box with corner " << a_.str() << " and " << b_.str() << "is degenerate";
+
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create axis aligned box",
+ s.str());
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Box::str(bool verbose) const
+{
+ std::stringstream s;
+
+ if (verbose)
+ {
+ s << "<Box with first corner at (" << a.str(true) << ") "
+ << "and second corner at (" << b.str(true) << ")>";
+ }
+ else
+ {
+ s << "Box(" << a.str(false) << ", " << b.str(false) << ")";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+// Cone
+//-----------------------------------------------------------------------------
+Cylinder::Cylinder(dolfin::Point top,
+ dolfin::Point bottom,
+ double top_radius,
+ double bottom_radius,
+ std::size_t segments)
+ : _top(top), _bottom(bottom), _top_radius(top_radius),
+ _bottom_radius(bottom_radius), _segments(segments)
+{
+ if (dolfin::near(top_radius, 0.0) && dolfin::near(bottom_radius, 0.0))
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create cylinder",
+ "Cylinder with zero thickness");
+ }
+
+ if (top.distance(bottom) < DOLFIN_EPS)
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create cylinder",
+ "Cylinder with zero length");
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Cylinder::str(bool verbose) const
+{
+ std::stringstream s;
+ if (verbose)
+ {
+ s << "<Cylinder with top at " << _top << ", top radius " << _top_radius
+ << " and bottom at " << _bottom << ", bottom radius "
+ << _bottom_radius << ", with " << _segments << " segments>";
+ }
+ else
+ {
+ s << "Cylinder( " << _top << ", " << _bottom << ", " << _top_radius
+ << ", " << _bottom_radius << " )";
+ }
+
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+Tetrahedron::Tetrahedron(dolfin::Point a,
+ dolfin::Point b,
+ dolfin::Point c,
+ dolfin::Point d)
+ : a(a), b(b), c(c), d(d)
+{
+ // TODO: Check validity of coordinates
+}
+//-----------------------------------------------------------------------------
+/// Informal string representation
+std::string Tetrahedron::str(bool verbose) const
+{
+ std::stringstream s;
+ if (verbose)
+ {
+ s << "<Tetrahedron with points at " << a << ", " << b << ", "
+ << c << ", " << d << ">";
+ }
+ else
+ {
+ s << "Tetrahedron( " << a << ", " << b << ", " << c << ", "
+ << d << ")";
+ }
+ return s.str();
+}
+//-----------------------------------------------------------------------------
+Surface3D::Surface3D(std::string filename)
+ : _filename(filename),
+ mesh(NULL),
+ vertex_tolerance(.0),
+ degenerate_tolerance(1e-12),
+ repair(false),
+ single_connected_component(false),
+ sharp_features_filter(-1),
+ first_facet(0),
+ flip_facets(false),
+ debug_dump("")
+{
+ // Do nothing
+}
+//-----------------------------------------------------------------------------
+Surface3D::Surface3D(std::shared_ptr<const dolfin::Mesh> m)
+ : _filename(""),
+ mesh(m),
+ vertex_tolerance(.0),
+ degenerate_tolerance(1e-12),
+ repair(false),
+ single_connected_component(false),
+ sharp_features_filter(-1),
+ first_facet(0),
+ cell_domain(0),
+ use_cell_domain(false)
+{}
+//-----------------------------------------------------------------------------
+Surface3D::Surface3D(std::shared_ptr<const dolfin::Mesh> m,
+ std::size_t cell_domain)
+ : _filename(""),
+ mesh(m),
+ vertex_tolerance(.0),
+ degenerate_tolerance(1e-12),
+ repair(false),
+ single_connected_component(false),
+ sharp_features_filter(-1),
+ first_facet(0),
+ cell_domain(cell_domain),
+ use_cell_domain(true)
+{}
+//-----------------------------------------------------------------------------
+std::string Surface3D::str(bool verbose) const
+{
+ return std::string("Surface3D from file ") + _filename;
+}
+//-----------------------------------------------------------------------------
+Ellipsoid::Ellipsoid(dolfin::Point center, double a, double b, double c, std::size_t segments)
+ : center(center), a(a), b(b), c(c), _segments(segments)
+{
+ if (a < DOLFIN_EPS || b < DOLFIN_EPS || c < DOLFIN_EPS)
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create ellipsoid",
+ "Ellipsoid with zero or negative semi-principal axis");
+ }
+
+ if (segments < 1)
+ {
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Create ellipsoid",
+ "Can't create ellipsoid with zero segments");
+ }
+}
+//-----------------------------------------------------------------------------
+std::string Ellipsoid::str(bool verbose) const
+{
+ std::stringstream ss;
+ if (verbose)
+ {
+ ss << "Ellipsoid centered at " << center.str() << " with semi-principal axes of lengths ";
+ ss << a << ", " << b << " and " << c;
+ }
+ else
+ {
+ ss << "Ellipsoid(" << center.str() << ", " << a << ", " << b << ", " << c << ")";
+ }
+ return ss.str();
+}
+//-----------------------------------------------------------------------------
+Extrude2D::Extrude2D(std::shared_ptr<CSGGeometry> geometry_2d, double z)
+ : geometry_2d(geometry_2d), z(z)
+{
+ if (geometry_2d->dim() != 2)
+ {
+ std::stringstream ss;
+ ss << "Expected geometry of dimension 2, got ";
+ ss << geometry_2d->dim();
+ dolfin::dolfin_error("CSGPrimitives3D.cpp",
+ "Extrude 2D geometry",
+ ss.str());
+}
+}
+//-----------------------------------------------------------------------------
+std::string Extrude2D::str(bool verbose) const
+{
+ std::stringstream ss;
+ ss << "Extruded 2D polygon, z = " << z;
+ if (verbose)
+ {
+ ss << geometry_2d->str(true);
+ }
+
+ return ss.str();
+}
+}
--- /dev/null
+// Copyright (C) 2014-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/DolfinMeshUtils.h>
+#include "FuzzyPointLocator.h"
+
+#include <dolfin/mesh/Cell.h>
+#include <dolfin/mesh/Vertex.h>
+#include <dolfin/mesh/Facet.h>
+#include <dolfin/mesh/MeshEditor.h>
+#include <dolfin/math/basic.h>
+
+#include <limits>
+
+namespace mshr
+{
+
+std::pair<double, double> DolfinMeshUtils::cell_volume_min_max(const dolfin::Mesh& m)
+{
+ std::pair<double, double> res(std::numeric_limits<double>::max(), 0.0);
+
+ for (dolfin::CellIterator cell(m); !cell.end(); ++cell)
+ {
+ const double v = cell->volume();
+ res.first = std::min(res.first, v);
+ res.second = std::max(res.second, v);
+ }
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+bool DolfinMeshUtils::has_isolated_vertices(const dolfin::Mesh& m)
+{
+ std::set<std::size_t> vertices;
+ for (dolfin::CellIterator cit(m); !cit.end(); ++cit)
+ {
+ const unsigned int* v = cit->entities(0);
+ for (std::size_t i = 0; i < cit->num_global_entities(0); i++)
+ {
+ vertices.insert(v[i]);
+ }
+ }
+
+ bool isolated_vertices = false;
+ for (std::size_t i = 0; i < m.num_vertices(); i++)
+ {
+ if (vertices.count(i) < 1)
+ {
+ log(dolfin::DBG, "Vertex %u has no incident cells", i);
+ isolated_vertices = true;
+ }
+ }
+
+ return isolated_vertices;
+}
+//-----------------------------------------------------------------------------
+bool DolfinMeshUtils::check_mesh(const dolfin::Mesh& m)
+{
+ return !has_isolated_vertices(m);
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh>
+ DolfinMeshUtils::extract_subdomain(std::shared_ptr<const dolfin::Mesh> mesh,
+ std::size_t cell_domain)
+{
+ dolfin_assert(mesh->geometry().dim() == 3);
+ dolfin_assert(mesh->topology().dim() == 3);
+
+ // Collect all vertices incident to all marked cells
+ std::map<std::size_t, std::size_t> collected_vertices;
+ std::size_t num_cells = 0;
+ for (const std::pair<std::size_t, std::size_t>& marker : mesh->domains().markers(3))
+ {
+ if (marker.second == cell_domain)
+ {
+ num_cells++;
+ dolfin::Cell c(*mesh, marker.second);
+ for (std::size_t i = 0; i < 4; i++)
+ {
+ const std::size_t s = collected_vertices.size();
+ collected_vertices.insert(std::make_pair(c.entities(0)[i], s));
+ }
+ }
+ }
+
+ std::shared_ptr<dolfin::Mesh> outmesh(new dolfin::Mesh);
+ dolfin::MeshEditor editor;
+ editor.open(*outmesh, dolfin::CellType::Type::tetrahedron, 3,3);
+
+ editor.init_vertices(collected_vertices.size());
+ for (std::pair<std::size_t, std::size_t> v : collected_vertices)
+ {
+ dolfin::Vertex existing_vertex(*mesh, v.first);
+ editor.add_vertex(v.second, existing_vertex.point());
+ }
+
+ editor.init_cells(num_cells);
+ std::size_t cell_counter = 0;
+ for (const std::pair<std::size_t, std::size_t>& marker : mesh->domains().markers(3))
+ {
+ if (marker.second == cell_domain)
+ {
+ const dolfin::Cell c(*mesh, marker.second);
+ const unsigned int* vertices = c.entities(0);
+ editor.add_cell(cell_counter,
+ collected_vertices[vertices[0]],
+ collected_vertices[vertices[1]],
+ collected_vertices[vertices[2]],
+ collected_vertices[vertices[3]]);
+
+ }
+ }
+
+ editor.close();
+ return outmesh;
+}
+
+
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh>
+DolfinMeshUtils::merge_meshes(std::shared_ptr<dolfin::Mesh> m1,
+ std::shared_ptr<dolfin::Mesh> m2,
+ int m1_marker,
+ int m2_marker,
+ int m1_boundary_marker,
+ int m2_boundary_marker,
+ int interface_marker)
+{
+ log(dolfin::TRACE, "Merge meshes");
+
+ if (m1->topology().dim() != m2->topology().dim() || m1->geometry().dim() != m2->geometry().dim())
+ dolfin::dolfin_error("DolfinMeshUtils.cpp",
+ "merging meshes",
+ "Meshes dimensions must match");
+
+ const std::size_t tdim = m1->topology().dim();
+
+ // Map vertex (geometric) points to index for m1
+ FuzzyPointMap vertex_map_inner(1e-14);
+ std::vector<std::size_t> map_to_mesh_index(m1->num_vertices());;
+
+ for (dolfin::VertexIterator v(*m1); !v.end(); ++v)
+ {
+ dolfin_assert(!vertex_map_inner.contains(v->point()));
+ const std::size_t map_index = vertex_map_inner.insert_point(v->point());
+ map_to_mesh_index[map_index] = v->global_index();
+ }
+
+ dolfin_assert(m1->num_vertices() == vertex_map_inner.size());
+
+ // Count number of shared vertices
+ // (We need this to initialize the mesh editor)
+ // TOOO: Store these to avoid another lookup?
+ std::size_t shared_vertices = 0;
+ for (dolfin::VertexIterator v(*m2); !v.end(); ++v)
+ {
+ if (vertex_map_inner.contains(v->point()))
+ shared_vertices++;
+ }
+
+ log(dolfin::TRACE, "Located %u shared vertices.", shared_vertices);
+
+ std::shared_ptr<dolfin::Mesh> outmesh(new dolfin::Mesh);
+ dolfin::MeshEditor editor;
+ editor.open(*outmesh, m1->type().cell_type(), tdim, tdim);
+ editor.init_vertices(m1->num_vertices() + m2->num_vertices() - shared_vertices);
+
+ // Add vertices from m1
+ for (dolfin::VertexIterator v(*m1); !v.end(); ++v)
+ {
+ editor.add_vertex(v->global_index(), v->point());
+ }
+
+ // Add vertices from m2
+ std::vector<std::size_t> m2_vertex_map(m2->num_vertices());
+
+ std::size_t m2_vertex_counter = m1->num_vertices();
+ for (dolfin::VertexIterator v(*m2); !v.end(); ++v)
+ {
+ std::size_t index;
+ if (vertex_map_inner.contains(v->point()))
+ {
+ const std::size_t map_index = vertex_map_inner.get_index(v->point());
+ index = map_to_mesh_index[map_index];
+ }
+ else
+ {
+ index = m2_vertex_counter;
+ editor.add_vertex(index, v->point());
+
+ m2_vertex_counter++;
+ }
+
+ m2_vertex_map[v->global_index()] = index;
+ }
+
+ editor.init_cells(m1->num_cells() + m2->num_cells());
+
+ std::size_t cell_counter = 0;
+
+ std::vector<std::size_t> vertices(tdim+1);
+
+ // Add cells from m1
+ for (dolfin::CellIterator c(*m1); !c.end(); ++c)
+ {
+ for (std::size_t i = 0; i < tdim+1; i++)
+ vertices[i] = c->entities(0)[i];
+
+ editor.add_cell(cell_counter,vertices);
+ cell_counter++;
+ }
+
+ for (dolfin::CellIterator c(*m2); !c.end(); ++c)
+ {
+ for (std::size_t i = 0; i < tdim+1; i++)
+ vertices[i] = m2_vertex_map[c->entities(0)[i]];
+
+ editor.add_cell(cell_counter,vertices);
+ cell_counter++;
+ }
+ editor.close();
+
+ return outmesh;
+
+ // Mark cells from
+ dolfin::MeshDomains &domain_markers = outmesh->domains();
+ domain_markers.init(tdim-1);
+ domain_markers.init(tdim);
+
+ for (std::size_t i = 0; i < outmesh->num_cells(); ++i)
+ {
+ if (m1->num_cells())
+ {
+ if (m1_marker >= 0)
+ domain_markers.set_marker(std::make_pair(i, m1_marker), tdim);
+ }
+ else
+ {
+ if (m2_marker >= 0)
+ domain_markers.set_marker(std::make_pair(i, m2_marker), tdim);
+ }
+ }
+
+ // Mark facets
+ // Boundary facets are marked with 1 or 2 depending on the source meshes the
+ // origined from. Facets shared by the two source meshes will be marked with
+ // 3.
+
+ // Compute facet - cell connectivity
+
+ outmesh->init(tdim-1, tdim);
+ for (dolfin::FacetIterator f(*outmesh); !f.end(); ++f)
+ {
+ if (f->exterior())
+ {
+ if (f->entities(tdim)[0] < m1->num_cells())
+ {
+ if (m1_boundary_marker >= 0)
+ domain_markers.set_marker(std::make_pair(f->index(), m1_boundary_marker), tdim-1);
+ }
+ else
+ {
+ if (m2_boundary_marker >= 0)
+ domain_markers.set_marker(std::make_pair(f->index(), m2_boundary_marker), tdim-1);
+ }
+ }
+ else
+ {
+ if (interface_marker >= 0)
+ {
+ const std::size_t cell1 = f->entities(tdim)[0];
+ const std::size_t cell2 = f->entities(tdim)[1];
+ if ( (cell1 < m1->num_cells() && cell2 >= m1->num_cells()) ||
+ (cell1 >= m1->num_cells() && cell2 < m1->num_cells()))
+ domain_markers.set_marker(std::make_pair(f->index(), interface_marker), tdim-1);
+ }
+ }
+ }
+
+ return outmesh;
+}
+
+}
--- /dev/null
+// Copyright (C) 2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Anders Logg 2016
+
+#ifndef FUZZY_POINT_SET_H
+#define FUZZY_POINT_SET_H
+
+#include <utility> // defines less operator for std::pair
+#include <map>
+#include <set>
+#include <algorithm>
+#include <iostream>
+#include <vector>
+#include <array>
+
+// TODO: Template over dimension.
+//template <std::size_t dim>
+class FuzzyPointMap
+{
+ public:
+ FuzzyPointMap(double tolerance)
+ : tolerance(tolerance)
+ {}
+ //-----------------------------------------------------------------------------
+ template <typename Point>
+ std::size_t insert_point(const Point& p)
+ //-----------------------------------------------------------------------------
+ {
+ const std::size_t index = get_index(p);
+ if (index == points.size())
+ {
+ // insert the points
+ points.push_back(std::array<double, 3>{{p[0], p[1], p[2]}});
+ maps[0].insert(std::make_pair(p[0], index));
+ maps[1].insert(std::make_pair(p[1], index));
+ maps[2].insert(std::make_pair(p[2], index));
+ }
+
+ return index;
+ }
+ //-----------------------------------------------------------------------------
+ template <typename Point>
+ std::size_t forced_insert_point(const Point& p)
+ //-----------------------------------------------------------------------------
+ {
+ const std::size_t index = points.size();
+
+ points.push_back(std::array<double, 3>{{p[0], p[1], p[2]}});
+ maps[0].insert(std::make_pair(p[0], index));
+ maps[1].insert(std::make_pair(p[1], index));
+ maps[2].insert(std::make_pair(p[2], index));
+
+ return index;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Point>
+ bool contains(const Point& p)
+ //-----------------------------------------------------------------------------
+ {
+ const std::size_t index = get_index(p);
+ return index < points.size();
+ }
+ //-----------------------------------------------------------------------------
+ const std::array<double, 3>& operator[](std::size_t i) const
+ //-----------------------------------------------------------------------------
+ {
+ return points[i];
+ }
+ //-----------------------------------------------------------------------------
+ const std::vector<std::array<double, 3>>& get_points() const
+ //-----------------------------------------------------------------------------
+ {
+ return points;
+ }
+ //-----------------------------------------------------------------------------
+ std::size_t size() const
+ //-----------------------------------------------------------------------------
+ {
+ return points.size();
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Point>
+ std::size_t get_index(const Point& p)
+ //-----------------------------------------------------------------------------
+ {
+ typedef std::multimap<double, std::size_t>::iterator iterator;
+
+ // Check if a nearby point exists
+ std::array<std::set<std::size_t>, 3> matches;
+ for (int i = 0; i < 3; i++)
+ {
+ iterator lb = maps[i].lower_bound(p[i]-tolerance);
+ iterator ub = maps[i].upper_bound(p[i]+tolerance);
+
+ if (lb != maps[i].end())
+ {
+ iterator it = lb;
+ while (it != ub)
+ {
+ matches[i].insert(it->second);
+ it++;
+ }
+ }
+ }
+
+ std::set<std::size_t> x_y_intersections;
+ std::set_intersection(matches[0].begin(), matches[0].end(),
+ matches[1].begin(), matches[1].end(),
+ std::inserter(x_y_intersections, x_y_intersections.begin()));
+
+ std::set<std::size_t> x_y_z_intersections;
+ std::set_intersection(matches[2].begin(), matches[2].end(),
+ x_y_intersections.begin(), x_y_intersections.end(),
+ std::inserter(x_y_z_intersections, x_y_z_intersections.begin()));
+
+ if (x_y_z_intersections.size() > 0)
+ return *x_y_z_intersections.begin();
+ else
+ return points.size();
+ }
+
+ private:
+ const double tolerance;
+
+ // Map from component of point to index
+ std::array<std::multimap<double, std::size_t>, 3> maps;
+ std::vector<std::array<double, 3> > points;
+};
+
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#include <mshr/GlobalInitializer.h>
+
+#include <CGAL/Random.h>
+
+#include <iostream>
+
+GlobalInitializer::GlobalInitializer()
+{
+ // std::cout << "Initializing globals" << std::endl;
+ #ifdef INIT_RANDOM_GENERATOR
+ // std::cout << "Using fixed random generator generator (seed=" << INIT_RANDOM_GENERATOR << ")" << std::endl;
+ CGAL::default_random = CGAL::Random( INIT_RANDOM_GENERATOR );
+ #endif
+
+}
+//-----------------------------------------------------------------------------
+GlobalInitializer::~GlobalInitializer()
+{
+
+}
+//-----------------------------------------------------------------------------
+GlobalInitializer _the_singleton;
+GlobalInitializer& GlobalInitializer::instance()
+{
+ return _the_singleton;
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg, Johannes Ring, 2012-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#include <mshr/MeshGenerator.h>
+#include <mshr/CSGGeometry.h>
+#include <mshr/CSGCGALMeshGenerator2D.h>
+#include <mshr/CSGCGALMeshGenerator3D.h>
+#include <mshr/TetgenMeshGenerator3D.h>
+
+#include <mshr/CSGCGALDomain2D.h>
+
+#include <dolfin/log/log.h>
+#include <dolfin/mesh/BoundaryMesh.h>
+
+
+namespace mshr
+{
+
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh> generate_mesh(std::shared_ptr<const CSGGeometry> geometry,
+ double resolution,
+ std::string backend)
+{
+ if (resolution <= 0)
+ {
+ dolfin::dolfin_error("MeshGenerator.cpp",
+ "generate from CSG geometry",
+ "Resolution argument must be positive");
+ }
+
+
+ if (geometry->dim() == 2)
+ {
+ if (backend != "cgal")
+ {
+ const std::string e = "Unknown mesh generator backend: " + backend + ". The only supported 2D backend is cgal.";
+ dolfin::dolfin_error("MeshGenerator.cpp",
+ "generate mesh of 2D geometry",
+ e);
+ }
+
+ CSGCGALMeshGenerator2D generator;
+
+ // Estimate the bounding circle radius from the geometry
+ const std::pair<dolfin::Point, double> bounding_circle = geometry->estimate_bounding_sphere();
+ const double segment_granularity = 2*bounding_circle.second/resolution;
+ const double cell_size = 2.0*bounding_circle.second/resolution;
+
+ // Calculate surface representation
+ log(dolfin::TRACE, "Request cell size: %f", cell_size);
+ generator.parameters["mesh_resolution"] = -1.0;
+ generator.parameters["cell_size"] = cell_size;
+
+ std::shared_ptr<CSGCGALDomain2D> total_domain(new CSGCGALDomain2D(geometry, segment_granularity));
+ std::vector<std::pair<std::size_t, std::shared_ptr<const CSGCGALDomain2D>>> subdomain_geometries;
+ for (const std::pair<std::size_t, std::shared_ptr<const CSGGeometry>>& subdomain : geometry->get_subdomains())
+ {
+ subdomain_geometries.push_back(std::make_pair(subdomain.first,
+ std::shared_ptr<CSGCGALDomain2D>(new CSGCGALDomain2D(subdomain.second, segment_granularity))));
+ }
+ return generator.generate(total_domain, subdomain_geometries);
+ }
+ else if (geometry->dim() == 3)
+ {
+ std::shared_ptr<CSGCGALDomain3D> domain(new CSGCGALDomain3D(geometry));
+ domain->ensure_meshing_preconditions();
+
+ if (backend == "cgal")
+ {
+ CSGCGALMeshGenerator3D generator;
+ generator.parameters["mesh_resolution"] = resolution;
+ return generator.generate(std::move(domain));
+ }
+ else if (backend == "tetgen")
+ {
+ TetgenMeshGenerator3D generator;
+ generator.parameters["mesh_resolution"] = resolution;
+ return generator.generate(std::move(domain));
+ }
+ else
+ {
+ std::string e = "Unknown mesh generator backend: " + backend;
+ dolfin::dolfin_error("MeshGenerator.cpp",
+ "Generator mesh of 3D geometry",
+ e);
+ return std::shared_ptr<dolfin::Mesh>();
+ }
+ }
+ else
+ {
+ dolfin::dolfin_error("MeshGenerator.cpp",
+ "create mesh from CSG geometry",
+ "Unhandled geometry dimension %d", geometry->dim());
+ return std::shared_ptr<dolfin::Mesh>();
+ }
+}
+
+}
--- /dev/null
+// Copyright (C) -2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#include <mshr/Meshes.h>
+#include <mshr/TetgenMeshGenerator3D.h>
+#include <mshr/CSGPrimitives3D.h>
+
+#include <dolfin/mesh/MeshPartitioning.h>
+
+namespace mshr
+{
+ UnitSphereMesh::UnitSphereMesh(std::size_t resolution)
+ : dolfin::Mesh()
+ {
+ // Receive mesh according to parallel policy
+ if (dolfin::MPI::is_receiver(this->mpi_comm()))
+ {
+ dolfin::MeshPartitioning::build_distributed_mesh(*this);
+ return;
+ }
+
+ std::shared_ptr<Sphere> s(new Sphere(dolfin::Point(0,0,0), 1.0, resolution));
+ std::shared_ptr<CSGCGALDomain3D> polyhedral_domain(new CSGCGALDomain3D(s));
+
+ TetgenMeshGenerator3D generator;
+ const double facet_area = 4.*DOLFIN_PI/polyhedral_domain->num_facets();
+ // compute edge length assuming perfect regular tetrahedrons
+ const double edge_length = 1.51967*std::sqrt(facet_area);
+ const double max_cell_volume = std::sqrt(2.)/12.*edge_length*edge_length*edge_length;
+ generator.parameters["max_tet_volume"] = max_cell_volume;
+ generator.parameters["preserve_surface"] = true;
+
+ std::shared_ptr<dolfin::Mesh> mesh = generator.generate(polyhedral_domain);
+ // dolfin::Mesh* m = static_cast<dolfin::Mesh>(this);
+ // *m = *mesh
+ dolfin::Mesh::operator=(*mesh);
+
+ // Broadcast mesh according to parallel policy
+ if (dolfin::MPI::is_broadcaster(this->mpi_comm()))
+ {
+ dolfin::MeshPartitioning::build_distributed_mesh(*this);
+ return;
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/OFFFileReader.h>
+
+#include <dolfin/geometry/Point.h>
+#include <dolfin/common/constants.h>
+#include <dolfin/log/LogStream.h>
+#include <dolfin/log/log.h>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+#include <boost/filesystem.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <algorithm>
+#include <string>
+#include <map>
+
+namespace
+{
+template<typename T>
+inline double convert_string(const std::string& s)
+{
+ std::istringstream is(s);
+ T val;
+ is >> val;
+
+ return val;
+}
+
+// get next line of file and trim away whitespace
+inline void get_next_line(std::ifstream& file, std::string& line, std::size_t &lineno)
+{
+ do
+ {
+ std::getline(file, line);
+ boost::algorithm::trim(line);
+ lineno++;
+ } while ( !file.eof() && line == "");
+}
+} // end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+
+void OFFFileReader::read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets)
+{
+
+ dolfin::log(dolfin:: TRACE, "Reading surface from %s ", filename.c_str());
+
+ vertices.clear();
+ facets.clear();
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+
+ std::ifstream file(filename.c_str());
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("OFFFileReader.cpp",
+ "open .off file to read 3D surface",
+ "Failed to open file");
+ }
+
+ std::string line;
+ std::size_t lineno = 0;
+ const boost::char_separator<char> sep(" ");
+
+ // Read the first line and trim away whitespaces
+ get_next_line(file, line, lineno);
+
+ if (line != "OFF")
+ {
+ dolfin::dolfin_error("OFFFileReader.cpp",
+ "open .off file to read 3D surface",
+ "File does not start with \"OFF\" (line %u", lineno);
+ }
+
+ get_next_line(file, line, lineno);
+
+ // Read number of vertices and facets
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ const std::size_t num_vertices = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ const std::size_t num_facets = convert_string<std::size_t>(*tok_iter);
+
+ vertices.reserve(num_vertices);
+ facets.reserve(num_facets);
+
+ get_next_line(file, line, lineno);
+
+ // Reader vertices
+ for (std::size_t i = 0; i < num_vertices; i++)
+ {
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ std::array<double, 3> vertex;
+ vertex[0] = convert_string<double>(*tok_iter);
+ tok_iter++;
+ vertex[1] = convert_string<double>(*tok_iter);
+ tok_iter++;
+ vertex[2] = convert_string<double>(*tok_iter);
+ tok_iter++;
+
+ dolfin_assert(tok_iter == tokens.end());
+
+ vertices.push_back(vertex);
+ get_next_line(file, line, lineno);
+ }
+
+ // Read facets
+ for (std::size_t i = 0; i < num_facets; i++)
+ {
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ const std::size_t v = convert_string<std::size_t>(*tok_iter);
+ if (v != 3)
+ dolfin::dolfin_error("OFFFileReader.cpp",
+ "reading off file",
+ "facet is not triangular");
+ tok_iter++;
+
+ std::array<std::size_t, 3> facet;
+ facet[0] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ facet[1] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+ facet[2] = convert_string<std::size_t>(*tok_iter);
+ tok_iter++;
+
+ dolfin_assert(tok_iter == tokens.end());
+
+ facets.push_back(facet);
+ get_next_line(file, line, lineno);
+ }
+}
+//-----------------------------------------------------------------------------
+void OFFFileReader::write(const std::string filename,
+ const std::vector<std::array<double, 3> >& vertices,
+ const std::vector<std::array<std::size_t, 3> >& facets)
+{
+ std::ofstream file(filename);
+ file.precision(6);
+
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("OFFFileReader.cpp",
+ "open file to write off data",
+ "Failed to open file");
+ }
+
+ file << "OFF " << vertices.size() << " " << facets.size() << " 0" << std::endl << std::endl;
+ for (const std::array<double, 3>& v : vertices)
+ {
+ file << v[0] << " " << v[1] << " " << v[2] << std::endl;
+ }
+
+ for (const std::array<std::size_t, 3>& f : facets)
+ {
+ file << "3 " << f[0] << " " << f[1] << " " << f[2] << std::endl;
+ }
+}
+}
--- /dev/null
+// Copyright (C) 2017 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+
+#ifndef POLYGON_UTILS_H__
+#define POLYGON_UTILS_H__
+
+class PolygonUtils
+{
+ public:
+
+ // Computes the orientation, assuming that it is well defined
+ static bool ccw(const std::vector<dolfin::Point>& vertices)
+ {
+ double signed_area = 0.0;
+
+ dolfin::Point prev = vertices.back();
+ for (std::vector<dolfin::Point>::const_iterator it = vertices.begin(),
+ v_end = vertices.end();
+ it != v_end;
+ ++it)
+ {
+ signed_area += (prev.x()*it->y())-(it->x()*prev.y());
+ prev = *it;
+ }
+
+ return signed_area > 0;
+ }
+};
+
+
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+
+#ifndef POLYHEDRAL_MULTICOMPONENT_MESH_DOMAIN_WITH_FEATURES_3_H
+#define POLYHEDRAL_MULTICOMPONENT_MESH_DOMAIN_WITH_FEATURES_3_H
+
+#include "FuzzyPointLocator.h"
+#include "Polyhedron_utils.h"
+#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
+
+#include <dolfin/log/log.h>
+//-----------------------------------------------------------------------------
+// This class reimplements Construct_initial_points (from Polyhedral_mesh_domain)
+// in order to make sure that all disconnected parts of the polyhedron are
+// sufficiently covered. Otherwise the meshing algorithm may miss them entirely.
+template< typename IGT_ >
+class Polyhedral_multicomponent_mesh_domain_with_features_3
+ : public CGAL::Polyhedral_mesh_domain_with_features_3< IGT_ >
+{
+ public:
+ typedef typename CGAL::Polyhedral_mesh_domain_with_features_3< IGT_ > Base;
+ typedef typename Base::Polyhedron Polyhedron;
+
+ // Passing the edge size with the constructor is a workaround. Ideally CGAL should pass it
+ // when calling construct_initial_points
+ Polyhedral_multicomponent_mesh_domain_with_features_3(const Polyhedron& p, double edge_size)
+ : Base(p), edge_size(edge_size)
+ {}
+
+ ~Polyhedral_multicomponent_mesh_domain_with_features_3(){}
+
+ struct Construct_initial_points
+ {
+ Construct_initial_points(const Polyhedral_multicomponent_mesh_domain_with_features_3& domain,
+ double edge_size)
+ : r_domain_(domain), edge_size(edge_size) {}
+
+ template<class OutputIterator>
+ OutputIterator operator()(OutputIterator pts, const int n = 8) const;
+
+ private:
+ const Polyhedral_multicomponent_mesh_domain_with_features_3& r_domain_;
+ const double edge_size;
+ };
+
+ Construct_initial_points construct_initial_points_object() const
+ {
+ return Construct_initial_points(*this, edge_size);
+ }
+
+ private :
+ const double edge_size;
+};
+//-----------------------------------------------------------------------------
+template<typename Set, typename Polyhedron>
+ void recursive_insert(Set& set,
+ std::set<typename Polyhedron::Vertex_const_handle>& visited,
+ typename Polyhedron::Vertex_const_handle v,
+ std::size_t n)
+{
+ std::pair<typename std::set<typename Polyhedron::Vertex_const_handle>::iterator, bool> v_insert = visited.insert(v);
+
+ // If vertex is already visited, then return
+ if (!v_insert.second)
+ {
+ return;
+ }
+
+ // Add point to set.
+ set.insert_point(v->point());
+
+ typename Polyhedron::Halfedge_around_vertex_const_circulator start = v->vertex_begin(), current = start;
+ do
+ {
+ if ( set.size() >= n )
+ break;
+
+ recursive_insert<Set, Polyhedron>(set, visited, current->opposite()->vertex(), n);
+
+ current++;
+ } while (current != start);
+}
+
+//-----------------------------------------------------------------------------
+template<typename IGT_>
+template<class OutputIterator>
+OutputIterator
+Polyhedral_multicomponent_mesh_domain_with_features_3<IGT_>::
+Construct_initial_points::operator()(OutputIterator pts, const int n) const
+{
+ typedef typename Polyhedral_multicomponent_mesh_domain_with_features_3::Polyhedron Polyhedron;
+ typedef typename Polyhedron::Point_3 Point_3;
+ typedef typename Polyhedron::Vertex_const_handle Vertex_const_handle;
+
+ const std::vector<Polyhedron>& poly_ = r_domain_.polyhedra();
+ if (poly_.size() != 1)
+ {
+ dolfin::dolfin_error("Polyhedral_multicomponent_mesh_domain_with_features_3.h",
+ "Accessing polyhedra for domain",
+ "Unexpected number of polyhedrons! (%f)", poly_.size());
+ }
+ const Polyhedron& P = poly_[0];
+ std::list<Vertex_const_handle> components;
+ mshr::PolyhedronUtils::get_disconnected_components(P, std::back_inserter(components));
+
+ // Collect inserted points in a set with a fuzzy comparison operator
+ // to ensure no points closer than the tolerance are inserted.
+ std::set<Vertex_const_handle> visited;
+
+ // TODO: Tune this parameter
+ const double tolerance = edge_size*3;
+ FuzzyPointMap inserted_points(tolerance);
+
+ std::size_t current_index;
+ {
+ // get corners
+ std::vector<std::pair<int, Point_3> > corners;
+ r_domain_.get_corners(std::back_inserter(corners));
+ current_index = corners.size();
+ current_index++;
+ for (const std::pair<int, Point_3>& c : corners)
+ {
+ inserted_points.forced_insert_point(c.second);
+ }
+ }
+
+ // Insert n surface points from each disconnected component
+ for (Vertex_const_handle v : components)
+ {
+ recursive_insert<FuzzyPointMap, Polyhedron>(inserted_points,
+ visited,
+ v,
+ n+inserted_points.size());
+ }
+
+ // for (auto it = inserted_points.begin(); it != inserted_points.end(); it++)
+ for (const std::array<double, 3>& p : inserted_points.get_points())
+ {
+
+ *pts = std::make_pair(Point_3(p[0], p[1], p[2]), current_index);
+ pts++;
+ current_index++;
+ }
+
+ return pts;
+}
+//-----------------------------------------------------------------------------
+#endif
--- /dev/null
+// Copyright (C) 2014-2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+
+#ifndef POLYHEDRON_UTILS_H__
+#define POLYHEDRON_UTILS_H__
+
+#include <dolfin/math/basic.h>
+
+#include <CGAL/basic.h>
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Simple_cartesian.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
+//#include <CGAL/Self_intersection_polyhedron_3.h>
+#include <CGAL/linear_least_squares_fitting_3.h>
+#include <CGAL/Constrained_Delaunay_triangulation_2.h>
+#include <CGAL/Triangulation_vertex_base_with_info_2.h>
+#include <CGAL/Polyhedron_incremental_builder_3.h>
+#include <CGAL/convex_hull_3.h>
+#include <CGAL/corefinement_operations.h>
+#include <CGAL/Aff_transformation_3.h>
+#include <CGAL/Delaunay_mesher_no_edge_refinement_2.h>
+#include <CGAL/Delaunay_mesh_face_base_2.h>
+#include <CGAL/Delaunay_mesh_size_criteria_2.h>
+
+#include <cmath>
+#include <deque>
+#include <fstream>
+
+namespace mshr
+{
+
+class PolyhedronUtils
+{
+ public:
+
+ //-----------------------------------------------------------------------------
+ template<typename CDT>
+ static void dump_2D_triangulation(const CDT& cdt, std::string filename)
+ {
+ std::cout << "Dumping 2D triangulation" << std::endl;
+
+ // Count valid cells and connected vertices
+ std::size_t num_cells = 0;
+ std::map<typename CDT::Vertex_handle, std::size_t> vertex_map;
+
+ for (typename CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ if (cgal_cell->is_in_domain())
+ {
+ num_cells++;
+
+ for (std::size_t i = 0; i < 3; i++)
+ {
+ const typename CDT::Vertex_handle v = cgal_cell->vertex(i);
+ if (vertex_map.count(v) == 0)
+ {
+ const std::size_t s = vertex_map.size();
+ vertex_map[v] = s;
+ }
+ }
+ }
+ }
+
+ std::cout << "Adding " << vertex_map.size() << " vertices and " << num_cells << " cells" << std::endl;
+
+ std::ofstream outfile(filename);
+ outfile << std::setprecision(16);
+ outfile << "OFF" << std::endl;
+ outfile << vertex_map.size() << " " << num_cells << " 0" << std::endl;
+ outfile << std::endl;
+
+ std::vector<typename CDT::Vertex_handle> vertices(vertex_map.size());
+ for (const std::pair<typename CDT::Vertex_handle, std::size_t>& vertex : vertex_map)
+ vertices[vertex.second] = vertex.first;
+
+ for (const typename CDT::Vertex_handle v : vertices)
+ {
+ outfile << v->point()[0] << " "
+ << v->point()[1] << " 0 " << std::endl;
+ }
+
+ for (typename CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ // Add cell if it is in the domain
+ if (cgal_cell->is_in_domain())
+ {
+ outfile << "3 "
+ << vertex_map[cgal_cell->vertex(0)] << " "
+ << vertex_map[cgal_cell->vertex(1)] << " "
+ << vertex_map[cgal_cell->vertex(2)] << std::endl;
+ }
+ }
+ }
+ //-----------------------------------------------------------------------------
+ // Scans the vertices of the polyhedron the polyhedron and returns a
+ // Polyhedron::Vertex_const_handle for each disconnected component.
+ template <typename Polyhedron, typename OutputIterator>
+ static void get_disconnected_components(const Polyhedron& p, OutputIterator it)
+ {
+ //typedef Polyhedron Polyhedron_t;
+ typedef typename Polyhedron::Halfedge_around_vertex_const_circulator HV_const_circulator;
+ typedef typename Polyhedron::Vertex_const_handle Vertex_const_handle;
+
+ // store all vertices in a set
+ std::set<Vertex_const_handle> v;
+ for (typename Polyhedron::Vertex_const_iterator vit = p.vertices_begin();
+ vit != p.vertices_end(); vit++)
+ v.insert(vit);
+
+ while (!v.empty())
+ {
+ // Add the component to the output
+ typename std::set<Vertex_const_handle>::iterator start_it = v.begin();
+ Vertex_const_handle start = *start_it;
+
+ *it = start;
+ it++;
+
+ // Remove all vertices belonging to component from v
+ std::deque<Vertex_const_handle> queue;
+ queue.push_back(start);
+ while (!queue.empty())
+ {
+ const Vertex_const_handle current = queue.front();
+ queue.pop_front();
+
+ if (v.count(current) > 0)
+ {
+ v.erase(current);
+
+ const HV_const_circulator h_start = current->vertex_begin();
+ HV_const_circulator h_current = h_start;
+ do
+ {
+ queue.push_back(h_current->opposite()->vertex());
+ h_current++;
+ } while (h_current != h_start);
+ }
+ }
+ }
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::vector<typename Polyhedron::Halfedge_handle>
+ get_holes(Polyhedron& P)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ P.normalize_border();
+
+ std::set<Halfedge_handle> border_edges;
+ for (typename Polyhedron::Halfedge_iterator hit = P.border_halfedges_begin(); hit != P.halfedges_end(); hit++)
+ {
+ Halfedge_handle b = hit;
+ dolfin_assert(b->is_border_edge());
+ if (b->is_border())
+ border_edges.insert(b);
+ else if (b->opposite()->is_border())
+ border_edges.insert(b->opposite());
+ else
+ dolfin_assert(false);
+ }
+
+ std::vector<Halfedge_handle> border_begins;
+ while (!border_edges.empty())
+ {
+ Halfedge_handle current = *(border_edges.begin());
+ border_begins.push_back(current);
+
+ std::size_t counter = 0;
+ const Halfedge_handle start = current;
+ do
+ {
+ dolfin_assert(border_edges.count(current) == 1);
+ border_edges.erase(current);
+ counter++;
+ current = current->next();
+ } while (current != start);
+ }
+
+ return std::move(border_begins);
+ }
+ //-----------------------------------------------------------------------------
+ // Count the number of edges in a facet or along a hole
+ template<typename Halfedge_handle>
+ static std::size_t edge_count(Halfedge_handle h)
+ {
+ Halfedge_handle current;
+ std::size_t counter = 0;
+ do
+ {
+ counter++;
+ current = current->next();
+ } while (current != h);
+
+ return counter;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static typename Polyhedron::Traits::Triangle_3 get_facet_triangle(typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+
+ dolfin_assert(h->facet()->is_triangle());
+
+ return Triangle_3(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static double cos_min_angle(typename Polyhedron::Traits::Vector_3 v,
+ typename Polyhedron::Halfedge_handle h1,
+ typename Polyhedron::Halfedge_handle h2)
+ {
+ v /= v.squared_length();
+
+ double max = 0;
+ typename Polyhedron::Halfedge_handle current = h1;
+ do
+ {
+ typename Polyhedron::Traits::Vector_3 w(current->vertex()->point(),
+ current->next()->vertex()->point());
+ w /= std::sqrt(CGAL::to_double(w.squared_length()));
+ max = std::max(std::abs(CGAL::to_double(w*v)));
+ } while (current != h2);
+
+ return max;
+ }
+ //-----------------------------------------------------------------------------
+ // Given a facet and a vertex (assumed to be incident to the facet), find the
+ // corresponding halfedge
+ template<typename Polyhedron>
+ static typename Polyhedron::Halfedge_handle
+ find_edge(typename Polyhedron::Vertex_handle v,
+ typename Polyhedron::Face_handle f)
+ {
+ dolfin_assert(v != typename Polyhedron::Vertex_handle());
+ dolfin_assert(f != typename Polyhedron::Face_handle());
+
+ typename Polyhedron::Halfedge_around_vertex_circulator start = v->vertex_begin();
+ typename Polyhedron::Halfedge_around_vertex_circulator current = start;
+
+ do
+ {
+ if (current->facet() == f)
+ {
+ dolfin_assert(current->vertex() == v && current->facet() == f);
+ return current;
+ }
+
+ current++;
+ } while(current != start);
+
+ dolfin_assert(false);
+ return typename Polyhedron::Halfedge_handle();
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static typename Polyhedron::Facet_handle
+ find_common_facet(typename Polyhedron::Vertex_handle v0,
+ typename Polyhedron::Vertex_handle v1,
+ typename Polyhedron::Vertex_handle v2)
+ {
+ typedef typename Polyhedron::Halfedge_around_vertex_circulator He_circulator;
+ std::set<typename Polyhedron::Facet_handle> facets0;
+ {
+ He_circulator start = v0->vertex_begin();
+ He_circulator current = start;
+ do
+ {
+ if (!current->is_border())
+ facets0.insert(current->facet());
+
+ current++;
+ } while (current != start);
+ }
+
+ std::set<typename Polyhedron::Facet_handle> facets1;
+ {
+ He_circulator start = v1->vertex_begin();
+ He_circulator current = start;
+ do
+ {
+ if (!current->is_border() && facets0.count(current->facet()) > 0)
+ facets1.insert(current->facet());
+
+ current++;
+ } while (current != start);
+ }
+
+ std::set<typename Polyhedron::Facet_handle> facets2;
+ {
+ He_circulator start = v2->vertex_begin();
+ He_circulator current = start;
+ do
+ {
+ if (!current->is_border() && facets1.count(current->facet()) > 0)
+ facets2.insert(current->facet());
+
+ current++;
+ } while (current != start);
+ }
+
+ dolfin_assert(facets2.size() < 2);
+ dolfin_assert(facets2.size() > 0);
+
+ return *(facets2.begin());
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void insert_edge(Polyhedron& P,
+ typename Polyhedron::Vertex_handle h,
+ typename Polyhedron::Vertex_handle g,
+ typename Polyhedron::Facet_handle f)
+ {
+ //std::pair<typename Polyhedron::Halfedge_handle, typename Polyhedron::Halfedge_handle>
+ //edges = find_edges(h_edge, g_edge);
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ Halfedge_handle h_edge, g_edge;
+ const Halfedge_handle start = g->halfedge();
+ Halfedge_handle current = start;
+ do
+ {
+ if (current->vertex() == h)
+ h_edge = current;
+ else if(current->vertex() == g)
+ g_edge == current;
+ } while (current != start);
+
+ dolfin_assert(h_edge != Halfedge_handle() && g_edge != Halfedge_handle());
+ P.split_facet(h_edge, g_edge);
+ dolfin_assert(P.is_valid());
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Triangle_3>
+ static double get_triangle_cos_angle(Triangle_3 t1,
+ Triangle_3 t2)
+ {
+ typedef typename CGAL::Kernel_traits<Triangle_3>::Kernel::Vector_3 Vector_3;
+
+ const Vector_3 v1 = t1.supporting_plane().orthogonal_vector();
+ const Vector_3 v2 = t2.supporting_plane().orthogonal_vector();
+
+ return CGAL::to_double((v1*v2)/std::sqrt(CGAL::to_double(v1.squared_length()*v2.squared_length())));
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static double get_edge_cos_angle(typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+
+ // std::cout << "get edge cos theta" << std::endl;
+
+ Vector_3 h1_vec(h->vertex()->point(), h->prev()->vertex()->point());
+ h1_vec = h1_vec/std::sqrt(CGAL::to_double(h1_vec.squared_length()));
+
+ // std::cout << "h1_vec_normalized: " << h1_vec << std::endl;
+ Vector_3 h2_vec(h->vertex()->point(), h->next()->vertex()->point());
+ h2_vec = h2_vec/std::sqrt(CGAL::to_double(h2_vec.squared_length()));
+ // std::cout << "h2_vec_normalized: " << h2_vec << std::endl;
+ const double cos_theta = CGAL::to_double(h1_vec*h2_vec);
+ // std::cout << "Cos theta: " << cos_theta << std::endl;
+ return cos_theta;
+ }
+ //-----------------------------------------------------------------------------
+ // Compute the plane fit quality of the vertices from h1 to h2 both included
+ // Return fitting quality, max projection distance, max cos angle
+ template<typename Polyhedron>
+ static std::array<double, 3>
+ get_plane_fit(const typename Polyhedron::Halfedge_handle h1,
+ const typename Polyhedron::Halfedge_handle h2)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Plane_3 Plane_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+ typedef typename Polyhedron::Traits::FT FT;
+ typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel;
+ typedef typename InexactKernel::Plane_3 InexactPlane_3;
+ typedef typename InexactKernel::Point_3 InexactPoint_3;
+ //typedef typename InexactKernel::Segment_3 InexactSegment_3;
+ //typedef typename InexactKernel::Vector_3 InexactVector_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+
+ // std::cout << "Get plane fit" << std::endl;
+
+ // std::cout << "Polygon ";
+ std::vector<InexactPoint_3> points;
+ //std::vector<InexactSegment_3> segments;
+ Halfedge_handle current = h1;
+ do
+ {
+ const Point_3& p = current->vertex()->point();
+ // std::cout << ", " << p;
+ points.push_back(InexactPoint_3(CGAL::to_double(p[0]),
+ CGAL::to_double(p[1]),
+ CGAL::to_double(p[2])));
+ current = current->next();
+ } while (current != h2);
+
+ {
+ const Point_3& p = h2->vertex()->point();
+ // std::cout << ", " << p;
+ points.push_back(InexactPoint_3(CGAL::to_double(p[0]),
+ CGAL::to_double(p[1]),
+ CGAL::to_double(p[2])));
+ }
+
+ dolfin_assert(points.size() > 2);
+ // std::cout << "Size: " << points.size() << std::endl;
+ //std::cout << std::endl;
+ InexactPlane_3 fitting_plane_inexact;
+ const double fit_quality = CGAL::linear_least_squares_fitting_3(points.begin(),
+ points.end(),
+ fitting_plane_inexact,
+ CGAL::Dimension_tag<0>());
+ Plane_3 fitting_plane(fitting_plane_inexact.a(),
+ fitting_plane_inexact.b(),
+ fitting_plane_inexact.c(),
+ fitting_plane_inexact.d());
+ // std::cout << "Plane: " << fitting_plane << std::endl;
+ // std::cout << "Length of normal: " << fitting_plane.orthogonal_vector().squared_length() << std::endl;
+ const Vector_3 normal = fitting_plane.orthogonal_vector()/std::sqrt(CGAL::to_double(fitting_plane.orthogonal_vector().squared_length()));
+
+ FT max_distance = (h1->vertex()->point()-fitting_plane.projection(h1->vertex()->point())).squared_length();
+ FT max_angle = 0;
+
+ Halfedge_handle prev = h1;
+ current = h1->next();
+ do
+ {
+ const Vector_3 v = current->vertex()->point()-prev->vertex()->point();
+ const FT cos_angle = v/std::sqrt(CGAL::to_double(v.squared_length())) * normal;
+ const Point_3 projection = fitting_plane.projection(current->vertex()->point());
+ max_angle = std::max(max_angle, cos_angle);
+ max_distance = std::max(max_distance, (current->vertex()->point()-projection).squared_length());
+
+ prev = current;
+ current = current->next();
+ } while (prev != h2);
+
+ // std::cout << "Fit quality: " << fit_quality << ", max distance: " << max_distance << ", cos_angle: " << max_angle << std::endl;
+ return std::array<double, 3>{fit_quality, CGAL::to_double(max_distance), CGAL::to_double(max_angle)};
+ // return -max_distance;
+ //return CGAL::to_double(fit_quality - max_angle);
+ }
+ //-----------------------------------------------------------------------------
+ // Compute the fit quality heuristic of the vertices from h1 to h2 both
+ // included.
+
+ // Some experiences:
+ // * The plane fit quality as returned from
+ // CGAL::linear_least_squares_fitting_3() is not suitable here. It measures
+ // how distinct the best fitting plane is, rather than the actual quality of
+ // the fit.
+ // * The max angle between the segments and the normal work good is the hole
+ // has no more than one "kink".
+ // * The max distance from the points to their projection is also a pretty
+ // good measure, but needs to be normalized in some clever way if not used
+ // solely.
+ template<typename Polyhedron>
+ static double evaluate_hole_subdivision(const typename Polyhedron::Halfedge_handle h1,
+ const typename Polyhedron::Halfedge_handle h2)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef CGAL::Simple_cartesian<double> InexactKernel;
+ //typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel;
+ typedef typename InexactKernel::Plane_3 InexactPlane_3;
+ typedef typename InexactKernel::Point_3 InexactPoint_3;
+ typedef typename InexactKernel::Segment_3 InexactSegment_3;
+ typedef typename InexactKernel::Vector_3 InexactVector_3;
+
+ // double plane1fit;
+ double max_angle1 = 0;
+ InexactPlane_3 fitting_plane1;
+ double avg_distance_squared1 = 0;
+ {
+ std::vector<InexactSegment_3> segments;
+ Halfedge_handle current = h1;
+ do
+ {
+ const Point_3& p = current->vertex()->point();
+ const Point_3& next = current->next()->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])),
+ InexactPoint_3(CGAL::to_double(next[0]), CGAL::to_double(next[1]), CGAL::to_double(next[2]))));
+ current = current->next();
+ } while (current != h2);
+
+ {
+ const Point_3& p = h2->vertex()->point();
+ const Point_3& next = h1->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])),
+ InexactPoint_3(CGAL::to_double(next[0]), CGAL::to_double(next[1]), CGAL::to_double(next[2]))));
+ }
+
+ dolfin_assert(segments.size() > 2);
+
+ /* plane1fit = */ CGAL::linear_least_squares_fitting_3(segments.begin(),
+ segments.end(),
+ fitting_plane1,
+ CGAL::Dimension_tag<1>());
+
+ // std::cout << " Plane 1: " << fitting_plane1 << ", " << plane1fit << std::endl;
+
+ // Compute max angle between segment and fitting plane
+ const InexactVector_3 normal = fitting_plane1.orthogonal_vector();
+ // std::cout << "Length: " << normal.squared_length() << std::endl;
+ dolfin_assert(dolfin::near(normal.squared_length(), 1, DOLFIN_EPS_LARGE));
+ for (auto sit = segments.begin(); sit != segments.end(); sit++)
+ {
+ const InexactVector_3 v = InexactVector_3(*sit) / std::sqrt(sit->squared_length());
+ // std::cout << "Length: " << (v.squared_length()-1) << std::endl;
+ dolfin_assert(dolfin::near(v.squared_length(), 1, DOLFIN_EPS_LARGE));
+ max_angle1 = std::max(max_angle1, std::abs(v*normal));
+ // max_distance_squared1 = std::max(max_distance_squared1,
+ avg_distance_squared1 += InexactVector_3(sit->source(), fitting_plane1.projection(sit->source())).squared_length();
+ }
+ avg_distance_squared1 /= segments.size();
+ }
+
+ /* double plane2fit; */
+ InexactPlane_3 fitting_plane2;
+ double max_angle2 = 0;
+ //double max_distance_squared2 = 0;
+ double avg_distance_squared2 = 0;
+ {
+ std::vector<InexactSegment_3> segments;
+ Halfedge_handle current = h2;
+ do
+ {
+ const Point_3& p = current->vertex()->point();
+ const Point_3& next = current->next()->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])),
+ InexactPoint_3(CGAL::to_double(next[0]), CGAL::to_double(next[1]), CGAL::to_double(next[2]))));
+ current = current->next();
+ } while (current != h1);
+
+ const Point_3& p = h1->vertex()->point();
+ const Point_3& next = h2->vertex()->point();
+
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])),
+ InexactPoint_3(CGAL::to_double(next[0]), CGAL::to_double(next[1]), CGAL::to_double(next[2]))));
+
+ dolfin_assert(segments.size() > 2);
+ // std::cout << " Size: " << segments.size() << std::endl;
+
+ /* plane2fit = */ CGAL::linear_least_squares_fitting_3(segments.begin(),
+ segments.end(),
+ fitting_plane2,
+ CGAL::Dimension_tag<1>());
+
+ // std::cout << " Plane 1: " << fitting_plane1 << ", " << plane1fit << std::endl;
+
+ // Compute max angle between plane and segments
+ const InexactVector_3 normal = fitting_plane2.orthogonal_vector();
+ dolfin_assert(dolfin::near(normal.squared_length(), 1, DOLFIN_EPS_LARGE));
+ for (auto sit = segments.begin(); sit != segments.end(); sit++)
+ {
+ const InexactVector_3 v = InexactVector_3(*sit) / std::sqrt(sit->squared_length());
+ dolfin_assert(dolfin::near(v.squared_length(), 1, DOLFIN_EPS_LARGE));
+ max_angle2 = std::max(max_angle2, std::abs(v*normal));
+ /* max_distance_squared2 = std::max(max_distance_squared2, */
+ /* InexactVector_3(sit->source(), fitting_plane2.projection(sit->source())).squared_length()); */
+ avg_distance_squared2 += InexactVector_3(sit->source(), fitting_plane2.projection(sit->source())).squared_length();
+ }
+ avg_distance_squared2 /= segments.size();
+ }
+
+ // const double cos_angle = fitting_plane1.orthogonal_vector()*fitting_plane2.orthogonal_vector();
+ // std::cout << " Angle: " << cos_angle << "(" << acos(cos_angle)/(2*DOLFIN_PI)*360 << ")" << std::endl;
+ // std::cout << "Max angles: " << max_angle1 << "(" << acos(max_angle1)/(2*DOLFIN_PI)*360 << "), " << max_angle2 << " (" << acos(max_angle2)/(2*DOLFIN_PI)*360 << ")" << std::endl;
+ // return std::min(plane1fit, plane2fit); // - .04*cos_angle - .05*max_angle1 - .05*max_angle2;// + triangulation_extra;
+
+ //return (-avg_distance_squared1 - avg_distance_squared2)/std::max(max_angle1, max_angle2); ///std::min(plane1fit, plane2fit);
+ return -std::max(max_angle1, max_angle2);
+ }
+
+ //-----------------------------------------------------------------------------
+ // Compute the transformation that rotates a given vector (assumed to be of
+ // unit length) into (0,0,1)
+ template<typename Vector_3>
+ static CGAL::Aff_transformation_3<typename CGAL::Kernel_traits<Vector_3>::Kernel>
+ rotate_to_xy(Vector_3 a)
+ {
+ typedef typename CGAL::Kernel_traits<Vector_3>::Kernel::RT RT;
+ typedef typename CGAL::Aff_transformation_3<typename CGAL::Kernel_traits<Vector_3>::Kernel> Aff_transformation_3;
+
+ // Inner product of a and target vector (0,0,1)
+ const RT cos_theta = a[2];
+
+ // Cross product of a and target vector (0,0,1)
+ // const RT sine_theta = CGAL::sqrt(a[1]*a[1] + a[0]*a[0]);
+ const RT sine_theta = sqrt(CGAL::to_double(a[1]*a[1] + a[0]*a[0]));
+
+ const RT ux = a[1]/sine_theta;;
+ const RT uy = -a[0]/sine_theta;
+ const RT uz = 0;
+ dolfin_assert(CGAL::abs(ux*ux + uy*uy + uz*uz) - 1 < DOLFIN_EPS);
+
+ return Aff_transformation_3(
+ cos_theta+ux*ux*(1-cos_theta), ux*uy*(1-cos_theta)-uz*sine_theta, ux*uz*(1-cos_theta)+uy*sine_theta,
+ uy*ux*(1-cos_theta)+uz*sine_theta, cos_theta+uy*uy*(1-cos_theta), uy*uz*(1-cos_theta)-ux*sine_theta,
+ uz*ux*(1-cos_theta)-uy*sine_theta, uz*uy*(1-cos_theta)+ux*sine_theta, cos_theta+uz*uz*(1-cos_theta));
+ }
+ //-----------------------------------------------------------------------------
+ template <typename HDS, typename CDT>
+ class Add2DTriangulation : public CGAL::Modifier_base<HDS>
+ {
+ public:
+ Add2DTriangulation(const CDT& cdt,
+ const typename HDS::Traits::Aff_transformation_3& from_xy,
+ const typename CDT::Vertex_handle v0,
+ const typename CDT::Vertex_handle v1,
+ const typename HDS::Traits::Vector_3 displacement=typename HDS::Traits::Vector_3(CGAL::Null_vector()))
+ : cdt(cdt),
+ rotate_from_xy(from_xy),
+ displacement(displacement),
+ v0_2d(v0),
+ v1_2d(v1),
+ v0_initialized(false),
+ v1_initialized(false)
+ { }
+
+ void operator()(HDS& hds)
+ {
+ CGAL::Polyhedron_incremental_builder_3<HDS> builder(hds, true);
+
+ for (typename CDT::Finite_vertices_iterator cgal_vertex = cdt.finite_vertices_begin();
+ cgal_vertex != cdt.finite_vertices_end(); ++cgal_vertex)
+ {
+ cgal_vertex->info() = std::make_pair(typename HDS::Vertex_handle(),
+ std::numeric_limits<std::size_t>::max());
+ }
+
+ // Find the face incident to both v0 and v1
+ // We will use this to check the orientation of the faces when they are
+ // inserted into the 3D polyhedron
+ typename CDT::Face_handle f;
+
+ // Count valid cells and connected vertices
+ std::size_t num_cells = 0;
+ std::size_t num_vertices = 0;
+ for (typename CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ if (cgal_cell->is_in_domain())
+ {
+ num_cells++;
+
+ for (std::size_t i = 0; i < 3; i++)
+ {
+ typename CDT::Vertex_handle v = cgal_cell->vertex(i);
+ if (v->info().second == std::numeric_limits<std::size_t>::max())
+ {
+ v->info().second = num_vertices;
+ num_vertices++;
+ }
+ }
+
+ if (cgal_cell->has_vertex(v0_2d) && cgal_cell->has_vertex(v1_2d))
+ {
+ std::cout << "!!!! Found the face!!!" << std::endl;
+ std::cout << cgal_cell->index(v0_2d) << " " << cgal_cell->index(v1_2d) << std::endl;
+ f = cgal_cell;
+ }
+ }
+ }
+
+ // Check the orientation of the facets
+ const bool flip = ((f->index(v1_2d)+1)%3 == f->index(v0_2d));
+
+ const typename HDS::Traits::Vector_3 displacement_flipped = flip ? -displacement : displacement;
+
+ builder.begin_surface(num_vertices, num_cells);
+
+ std::cout << "Adding " << num_vertices << " vertices and " << num_cells << " cells" << std::endl;
+
+ // Add vertices
+ std::size_t vertex_index = 0;
+ for (typename CDT::Finite_vertices_iterator cgal_vertex = cdt.finite_vertices_begin();
+ cgal_vertex != cdt.finite_vertices_end(); ++cgal_vertex)
+ {
+ const typename CDT::Vertex_handle current = cgal_vertex;
+
+ // Transform point from xy plane to where it belongs in the polyhendron (2D point is EPICK)
+ typename HDS::Traits::Point_3 p = rotate_from_xy(typename HDS::Traits::Point_3(cgal_vertex->point()[0],
+ cgal_vertex->point()[1],
+ 0))+displacement;
+
+ /* std::cout << " 2D point: " << cgal_vertex->point() << " (" << z << ")" << std::endl; */
+ /* std::cout << " Rotated: " << p << std::endl; */
+ /* std::cout << " Index: " << vertex_index << std::endl; */
+// FRom remove-null-facets
+ /* // std::cout << "Now creating face" << std::endl; */
+
+ /* // This typedef for some reason gives a "unused local typedef" warning */
+ /* //from clang (...?) --> write out the typename in the statements */
+ /* //below. */
+ /* // typedef typename Halfedge::Base HBase; */
+ /* edges[0]->Halfedge::Base::set_next(edges[1]); */
+ /* decorator.set_prev(edges[1], edges[0]); */
+ /* edges[1]->Halfedge::Base::set_next(edges[2]); */
+ /* decorator.set_prev(edges[2], edges[1]); */
+ /* edges[2]->Halfedge::Base::set_next(edges[0]); */
+ /* decorator.set_prev(edges[0], edges[2]); */
+// end remove-null-facets
+
+ // Add vertex (convert point to EPECK)
+ typename HDS::Vertex_handle h = builder.add_vertex(p);
+ if (current == v0_2d)
+ {
+ v0 = h;
+ v0_initialized = true;
+ }
+ else if (current == v1_2d)
+ {
+ v1 = h;
+ v1_initialized = true;
+ }
+
+ // Attach index to vertex and increment
+ cgal_vertex->info().second = vertex_index++;
+ }
+
+ // Add cells to mesh and build domain marker mesh function
+ // std::size_t cell_index = 0;
+ for (typename CDT::Finite_faces_iterator cgal_cell = cdt.finite_faces_begin();
+ cgal_cell != cdt.finite_faces_end(); ++cgal_cell)
+ {
+ // Add cell if it is in the domain
+ if (cgal_cell->is_in_domain())
+ {
+ const typename CDT::Geom_traits::Triangle_2 t_2d(cgal_cell->vertex(0)->point(),
+ cgal_cell->vertex(1)->point(),
+ cgal_cell->vertex(2)->point());
+
+ // Transform point from xy plane to where it belongs in the polyhendron (2D point is EPICK)
+ typename HDS::Traits::Point_3 p0 = rotate_from_xy(typename HDS::Traits::Point_3(cgal_cell->vertex(0)->point()[0],
+ cgal_cell->vertex(0)->point()[1],
+ 0));
+
+ typename HDS::Traits::Point_3 p1 = rotate_from_xy(typename HDS::Traits::Point_3(cgal_cell->vertex(1)->point()[0],
+ cgal_cell->vertex(1)->point()[1],
+ 0));
+
+ typename HDS::Traits::Point_3 p2 = rotate_from_xy(typename HDS::Traits::Point_3(cgal_cell->vertex(2)->point()[0],
+ cgal_cell->vertex(2)->point()[1],
+ 0));
+
+ typename HDS::Traits::Triangle_3 t_3d(p0, p1, p2);
+
+ const double diff = sqrt(CGAL::to_double(t_3d.squared_area()))-CGAL::to_double(CGAL::abs(t_2d.area()));
+ if (std::abs(diff) > 1e-10)
+ std::cout << "Rotated triangle differs: " << diff << std::endl;
+
+
+
+ builder.begin_facet();
+ builder.add_vertex_to_facet(cgal_cell->vertex(0)->info().second);
+ if (flip)
+ {
+ builder.add_vertex_to_facet(cgal_cell->vertex(2)->info().second);
+ builder.add_vertex_to_facet(cgal_cell->vertex(1)->info().second);
+ /* std::cout << " Adding vertex: (" << cgal_cell->vertex(0)->info().second */
+ /* << ", " << cgal_cell->vertex(2)->info().second */
+ /* << ", " << cgal_cell->vertex(1)->info().second << std::endl; */
+ }
+ else
+ {
+ builder.add_vertex_to_facet(cgal_cell->vertex(1)->info().second);
+ builder.add_vertex_to_facet(cgal_cell->vertex(2)->info().second);
+ /* std::cout << " Adding vertex: (" << cgal_cell->vertex(0)->info().second */
+ /* << ", " << cgal_cell->vertex(1)->info().second */
+ /* << ", " << cgal_cell->vertex(2)->info().second << std::endl; */
+
+ }
+ new_facets.insert(builder.end_facet());
+ }
+ }
+
+ builder.end_surface();
+
+ dolfin_assert(v0_initialized);
+ dolfin_assert(v1_initialized);
+ }
+ const CDT& cdt;
+ const typename HDS::Traits::Aff_transformation_3 rotate_from_xy;
+ const typename HDS::Traits::Vector_3 displacement;
+ const typename CDT::Vertex_handle v0_2d, v1_2d;
+ typename HDS::Vertex_handle v0, v1;
+ bool v0_initialized, v1_initialized;
+ std::set<typename HDS::Halfedge_handle> new_facets;
+ };
+
+ /// Attempts to triangulate a polygon in 3d by projecting vertices into the
+ /// best fitting plane and triangulating in 2d.
+ /// Return the new added edges.
+ /// If the triangulation is not possible (the boundary self intersects in this 2d plane) then
+ /// the return vector is empty
+ template <typename Polyhedron>
+ static bool triangulate_polygon_3d(Polyhedron& P,
+ const typename Polyhedron::Halfedge_handle h,
+ bool check_for_intersections = true,
+ bool refine = true)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Vertex_handle Vertex_handle;
+ // typedef typename Polyhedron::Facet_handle Facet_handle;
+ typedef typename Polyhedron::Traits::FT FT;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::Segment_3 Segment_3;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+ typedef CGAL::Aff_transformation_3<typename Polyhedron::Traits> Aff_transformation_3;
+
+
+ typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel;
+ typedef typename InexactKernel::Plane_3 InexactPlane_3;
+ typedef typename InexactKernel::Point_3 InexactPoint_3;
+ typedef typename InexactKernel::Vector_3 InexactVector_3;
+ typedef typename InexactKernel::Point_2 InexactPoint_2;
+ // typedef typename InexactKernel::Segment_2 InexactSegment_2;
+ typedef typename InexactKernel::Segment_3 InexactSegment_3;
+ typedef typename InexactKernel::Triangle_2 InexactTriangle_2;
+
+ typedef CGAL::Triangulation_vertex_base_with_info_2<std::pair<Vertex_handle, std::size_t>, InexactKernel> Vb;
+ typedef CGAL::Delaunay_mesh_face_base_2<InexactKernel> Fb;
+ typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
+ typedef CGAL::No_intersection_tag Itag;
+ typedef CGAL::Constrained_Delaunay_triangulation_2<InexactKernel, TDS, Itag> CDT;
+ typedef CGAL::Delaunay_mesh_size_criteria_2<CDT> Mesh_criteria_2;
+ typedef CGAL::Delaunay_mesher_no_edge_refinement_2<CDT, Mesh_criteria_2> CGAL_Mesher_2;
+
+ std::cout << "Triangulating hole as 2d polygon" << std::endl;
+ dolfin_assert(P.is_valid());
+
+ {
+ std::cout << "Polygon ";
+ typename Polyhedron::Halfedge_handle current = h;
+ do
+ {
+ std::cout << current->vertex()->point() << ", ";
+ current = current->next();
+ } while (current != h);
+
+ std::cout << std::endl;
+ }
+
+ Aff_transformation_3 to_xy;
+ Vector_3 plane_normal;
+ {
+ // Compute the best fitting plane of the points of the hole
+ InexactPlane_3 fitting_plane;
+
+
+ std::vector<InexactSegment_3> boundary;
+ Halfedge_handle current = h;
+ do
+ {
+ const Point_3& p = current->vertex()->point();
+ const Point_3& next = current->next()->vertex()->point();
+ boundary.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])),
+ InexactPoint_3(CGAL::to_double(next[0]), CGAL::to_double(next[1]), CGAL::to_double(next[2]))));
+
+ current = current->next();
+ } while (current != h);
+
+ /* const double fit_quality = */
+ CGAL::linear_least_squares_fitting_3(boundary.begin(),
+ boundary.end(),
+ fitting_plane,
+ CGAL::Dimension_tag<1>());
+
+ // Compute rotation that will rotate the fitting plane to the xy plane
+ const InexactVector_3 orthogonal = fitting_plane.orthogonal_vector();
+ const Vector_3 orthogonal_exact(orthogonal[0], orthogonal[1], orthogonal[2]);
+ // std::cout << "Orthogonal vector: " << orthogonal << ", " << orthogonal.squared_length() << std::endl;
+ const Aff_transformation_3 rotation = rotate_to_xy(orthogonal_exact);
+ // std::cout << "Rotation: " << rotation << std::endl;
+ std::cout << "Rotated: " << rotation.transform(orthogonal_exact) << std::endl;
+ const InexactPoint_3 point_on_plane = fitting_plane.point();
+ const FT z = rotation.transform(Point_3(point_on_plane[0],
+ point_on_plane[1],
+ point_on_plane[2]))[2];
+
+ const Aff_transformation_3 zero_z(CGAL::Translation(), Vector_3(0,0,-z));
+
+ double max_cos_normal_angle = 0;
+ const InexactVector_3 normal = fitting_plane.orthogonal_vector();
+ std::cout << "Normal: " << plane_normal << std::endl;
+ plane_normal = Vector_3(normal.x(), normal.y(), normal.z());
+
+ double max_squared_distance = 0;
+ dolfin_assert(dolfin::near(normal.squared_length(), 1, DOLFIN_EPS_LARGE));
+
+ // Compute 2D bounding box
+ std::vector<InexactPoint_2> points_2D;
+ for (const InexactSegment_3& s : boundary)
+ {
+ const Point_3 current_exact(s.source()[0], s.source()[1], s.source()[2]);
+ const Point_3 rotated = rotation.transform(current_exact);
+ points_2D.push_back(InexactPoint_2(CGAL::to_double(rotated[0]),
+ CGAL::to_double(rotated[1])));
+ }
+ CGAL::Bbox_2 bbox = CGAL::bbox_2(points_2D.begin(), points_2D.end());
+ std::cout << "Bounding box: " << bbox << std::endl;
+
+ // InexactVector_3 prev = InexactVector_3(boundary[boundary.size()-1]) / std::sqrt(boundary[boundary.size()-1].squared_length());
+ for (const InexactSegment_3& s : boundary)
+ {
+ InexactVector_3 current = InexactVector_3(s) / std::sqrt(s.squared_length());
+ max_squared_distance = std::max(max_squared_distance, (s.source()-fitting_plane.projection(s.source())).squared_length());
+
+ // std::cout << "Length: " << sit->squared_length() << ", " << current.squared_length() << ", " << (current*normal) << std::endl;
+ dolfin_assert(dolfin::near(current.squared_length(), 1, DOLFIN_EPS_LARGE));
+ max_cos_normal_angle = std::max(max_cos_normal_angle, CGAL::abs(current*normal));
+ // prev = current;
+
+ const Point_3 current_exact(s.source()[0], s.source()[1], s.source()[2]);
+ const Point_3 rotated = rotation.transform(current_exact);
+ // bbox += InexactPoint_2(CGAL::to_double(rotated[0]), CGAL::to_double(rotated[1])).bbox();
+ // std::cout << " " << bbox << std::endl;
+ }
+
+ std::cout << "Max abs cos normal angle: " << max_cos_normal_angle << std::endl;
+ // std::cout << "Plane quality: " << fit_quality << std::endl;
+ std::cout << "Max distance: " << std::sqrt(max_squared_distance) << std::endl;
+
+ if (max_cos_normal_angle > .2)
+ {
+ std::cout << "ERROR: Rejecting 2d triangulating, max_cos_normal_angle: " << max_cos_normal_angle << std::endl;
+ return false;
+ }
+ if (max_squared_distance > 1e-8)
+ {
+ std::cout << "ERROR: Rejecting 2d triangulating, max squared_distance: " << max_squared_distance << std::endl;
+ return false;
+ }
+
+ const Aff_transformation_3 normalization(CGAL::Translation(), Vector_3(-(bbox.xmax()+bbox.xmin())/2,
+ -(bbox.ymax()+bbox.ymin())/2,
+ 0));
+
+ to_xy = normalization*zero_z*rotation;
+ }
+
+
+ // std::cout << "Rotate normal: " << rotation.transform(fitting_plane.orthogonal_vector()) << std::endl;
+ // dolfin_assert(dolfin::near(fitting_plane.orthogonal_vector().squared_length(), 1, DOLFIN_EPS_LARGE));
+ double max_z = 0.;
+
+ CDT cdt;
+
+ std::cout << "Projected polygon" << std::endl;
+ std::cout << "Polygon";
+
+ // Insert vertices into 2D triangulation
+ std::vector<typename CDT::Vertex_handle> vertices;
+ double max_squared_edge_length = 0;
+ double min_squared_edge_length = std::numeric_limits<double>::max();
+
+ {
+ Halfedge_handle current = h;
+ Point_3 prev = current->prev()->vertex()->point();
+ std::stringstream ss;
+ ss << "Polygon ";
+ do
+ {
+
+ const Point_3& p = current->vertex()->point();
+ // InexactPoint_3 p_inexact(CGAL::to_double(p[0]),
+// CGAL::to_double(p[1]),
+ // CGAL::to_double(p[2]));
+ // InexactPoint_3 p_projected = fitting_plane.projection(p_inexact);
+ // std::cout << " " << p_projected << ", ";
+
+ const double length_current = CGAL::to_double(Segment_3(prev, p).squared_length());
+ max_squared_edge_length = std::max(max_squared_edge_length, length_current);
+ min_squared_edge_length = std::min(min_squared_edge_length, length_current);
+ const Point_3 rotated = to_xy.transform(p);
+
+ max_z = std::max(max_z, CGAL::to_double(CGAL::abs(rotated.z())));
+ ss << " " << rotated << ", ";
+
+ const InexactPoint_2 p_2d(CGAL::to_double(rotated[0]), CGAL::to_double(rotated[1]));
+
+ // std::cout << " " << p_2d << ", ";
+
+ vertices.push_back(cdt.insert(p_2d));
+ vertices.back()->info().first = current->vertex();
+
+ prev = p;
+ current = current->next();
+ } while (current != h);
+
+ // std::cout << std::endl;
+ std::cout << std::endl;
+ std::cout << ss.str() << std::endl;
+ }
+
+ std::cout << "Size of points: " << vertices.size() << std::endl;
+ // std::cout << "z = " << z << std::endl;
+ std::cout << "Max z : " << max_z << std::endl;
+ std::cout << "Longest edge: " << max_squared_edge_length << std::endl;
+ std::cout << "Shortest edge: " << min_squared_edge_length << std::endl;
+
+
+ // Check if any of the edges intersect (before actually adding the
+ // constrained edges to the triangulation)
+#if 0
+ if (check_for_intersections)
+ {
+ for (std::size_t i = 0; i < vertices.size()-1; i++)
+ {
+ const Point_3& a = vertices[i]->info().first->point(), b = vertices[+1]->info().first->point();
+ const InexactSegment_2 s(vertices[i]->point(), vertices[i+1]->point());
+ const Segment_3 original(a, b);
+
+ const InexactSegment_3 s2(fitting_plane.projection(InexactPoint_3(CGAL::to_double(a[0]),
+ CGAL::to_double(a[1]),
+ CGAL::to_double(a[2]))),
+ fitting_plane.projection(InexactPoint_3(CGAL::to_double(b[0]),
+ CGAL::to_double(b[1]),
+ CGAL::to_double(b[2]))));
+
+ for (std::size_t j = i+1; j < vertices.size(); j++)
+ {
+ InexactSegment_2 s2(vertices[j]->point(), vertices[(j+1)%vertices.size()]->point());
+
+ const auto intersection = CGAL::intersection(s, s2);
+
+ if (intersection)
+ {
+ if (boost::get<InexactPoint_2>(&*intersection))
+ {
+ if (j != i+1 && i != (j+1)%vertices.size())
+ {
+ std::cout << "Non-neighbors (" << i << ", " << j << ")/" << vertices.size()
+ << " intersect in single point" << std::endl;
+
+ return false;
+ }
+ }
+ else if (boost::get<InexactSegment_2>(&*intersection))
+ {
+ std::cout << "Intersects in segment" << std::endl;
+ return false;
+ }
+ else
+ {
+ dolfin_assert(false);
+ return false;
+ }
+ } // end if intersection
+ } // end inner loop
+ } // end outer loop
+
+ // No edges intersect, so we can safely insert then as constraints to the
+ // triangulation
+ }
+#endif
+
+ // Insert the edges around the facet as constraints to the triangulation
+ for (std::size_t i = 0; i < vertices.size(); i++)
+ {
+ // std::cout << "Insert constraint: (" << i << ") " << vertices[i]->point() << ", (" << (i+1)%vertices.size() << ") " << vertices[(i+1)%vertices.size()]->point() << std::endl;
+ cdt.insert_constraint(vertices[i], vertices[(i+1)%vertices.size()]);
+ }
+
+ // std::cout << "Done triangulating" << std::endl;
+ // std::cout << "Num vertices: " << cdt.number_of_vertices() << std::endl;
+
+ if (refine)
+ {
+ // Create mesher
+ CGAL_Mesher_2 mesher(cdt);
+
+ // Set shape and size criteria
+ mesher.set_criteria(Mesh_criteria_2(.125, 2*std::sqrt(max_squared_edge_length)));
+
+ // No size criteria, only shape
+ //mesher.set_criteria(Mesh_criteria_2());
+
+ std::cout << "Max edge length: " << 2*std::sqrt(max_squared_edge_length) << std::endl;
+
+ // Refine CGAL mesh/triangulation
+ std::cout << "Refining 2D mesh" << std::endl;
+ mesher.refine_mesh();
+ }
+ else
+ {
+ for (auto f = cdt.finite_faces_begin(); f != cdt.finite_faces_end(); f++)
+ {
+ f->set_in_domain(true);
+ }
+ }
+
+ std::cout << "Done meshing. Num vertices: " << cdt.number_of_vertices() << std::endl;
+
+ {
+ double shortest_edge = std::numeric_limits<double>::max();
+ double smallest_triangle = std::numeric_limits<double>::max();
+ for (auto f = cdt.finite_faces_begin(); f != cdt.finite_faces_end(); f++)
+ {
+ if (f->is_in_domain())
+ {
+ for (int i = 0; i < 3; i++)
+ shortest_edge = std::min(shortest_edge,
+ CGAL::to_double((f->vertex(i)->point()-f->vertex((i+1)%3)->point()).squared_length()));
+ InexactTriangle_2 t(f->vertex(0)->point(),
+ f->vertex(1)->point(),
+ f->vertex(2)->point());
+ smallest_triangle = std::min(smallest_triangle, CGAL::to_double(CGAL::abs(t.area())));
+ }
+ }
+
+ std::cout << "Shortest edge in 2D triangulation: " << shortest_edge << std::endl;
+ std::cout << "Smallest triangle in 2D triangulation: " << smallest_triangle << std::endl;
+ }
+
+ //P.normalize_border();
+ // dolfin_assert(P.is_valid(false, 1));
+
+ // add the triangulation to the polyhedron
+
+ dump_2D_triangulation(cdt, "triangulation2D.off");
+
+ Add2DTriangulation<typename Polyhedron::HalfedgeDS, CDT> builder(cdt,
+ to_xy.inverse(),
+ vertices[0],
+ vertices[1],
+ plane_normal*.001);
+ P.delegate(builder);
+
+ std::cout << "Inserted: " << builder.new_facets.size() << " new facets" << std::endl;
+
+ // Find the border edge incident to "inserted h"
+ const typename Polyhedron::Halfedge_around_vertex_circulator start = builder.v0->vertex_begin();
+ typename Polyhedron::Halfedge_around_vertex_circulator current = start;
+
+ do
+ {
+ if (current->opposite()->vertex() == builder.v1)
+ {
+ std::cout << "Found the edge" << std::endl;
+ break;
+ }
+ std::cout << "Advancing" << std::endl;
+ current++;
+ } while (current != start);
+
+ const typename Polyhedron::Halfedge_handle h_new = current->is_border() ? current : current->opposite();
+
+ if (!h_new->is_border())
+ {
+ std::cout << "Is border edge: " << h_new->is_border_edge() << std::endl;
+ dolfin::dolfin_error("Polyhedron_utils.h",
+ "Locating border edge",
+ "locating border edge");
+ }
+
+ std::cout << "2D: " << vertices[0]->point() << std::endl;
+ std::cout << "Original: " << h->vertex()->point() << std::endl;
+ std::cout << "Original Next: " << h->next()->vertex()->point() << std::endl;
+ std::cout << "Original prev: " << h->prev()->vertex()->point() << std::endl;
+ std::cout << "Inserted: " << h_new->vertex()->point() << std::endl;
+ std::cout << "Inserted next: " << h_new->next()->vertex()->point() << std::endl;
+ std::cout << "Inserted prev: " << h_new->prev()->vertex()->point() << std::endl;
+ std::cout << "Vertex degree: " << h_new->vertex()->vertex_degree() << std::endl;
+
+
+ std::cout << "Joining loop" << std::endl;
+
+ {
+ Halfedge_handle a = h_new;
+ Halfedge_handle b = h;
+ double max_distance = 0.;
+ do
+ {
+ max_distance = std::max(max_distance, CGAL::to_double((a->vertex()->point()-b->vertex()->point()).squared_length()));
+
+ a = a->next();
+ b = b->prev();
+ } while (a != h_new);
+ std::cout << "Max distance between merged vertices: " << max_distance << std::endl;
+ }
+
+ {
+ double shortest_edge = std::numeric_limits<double>::max();
+ for (auto e = P.halfedges_begin(); e != P.halfedges_end(); e++)
+ {
+ shortest_edge = std::min(shortest_edge, CGAL::to_double((e->vertex()->point()-e->opposite()->vertex()->point()).squared_length()));
+ }
+
+ std::cout << "Shortest edge: " << shortest_edge << std::endl;
+ }
+
+ {
+ double smallest_triangle = std::numeric_limits<double>::max();;
+ for (auto f = P.facets_begin(); f != P.facets_end(); f++)
+ {
+ auto h = f->halfedge();
+ if (f->is_triangle())
+ {
+ typename Polyhedron::Traits::Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ smallest_triangle = std::min(smallest_triangle,
+ CGAL::to_double(t.squared_area()));
+ }
+ }
+ std::cout << "Smallest triangle before join: " << smallest_triangle << std::endl;
+ }
+
+
+ //
+ P.join_loop(h, h_new);
+
+ {
+ double shortest_edge = std::numeric_limits<double>::max();
+ for (auto e = P.halfedges_begin(); e != P.halfedges_end(); e++)
+ {
+ shortest_edge = std::min(shortest_edge, CGAL::to_double((e->vertex()->point()-e->opposite()->vertex()->point()).squared_length()));
+ }
+
+ std::cout << "Shortest edge after merge: " << shortest_edge << std::endl;
+ }
+
+ {
+ double smallest_triangle = std::numeric_limits<double>::max();;
+ for (auto f = P.facets_begin(); f != P.facets_end(); f++)
+ {
+ auto h = f->halfedge();
+ if (f->is_triangle())
+ {
+ typename Polyhedron::Traits::Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ smallest_triangle = std::min(smallest_triangle,
+ CGAL::to_double(t.squared_area()));
+ }
+ }
+ std::cout << "Smallest triangle: " << smallest_triangle << std::endl;
+ }
+
+
+
+ std::cout << "H degree: " << h->facet()->facet_degree() << std::endl;
+ std::cout << "Hole: " << (h->is_border_edge() ? "Yes" : "No") << std::endl;
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ template <typename Polyhedron>
+ static std::pair<double, double> evaluate_planarity(const typename Polyhedron::Halfedge_handle from,
+ const typename Polyhedron::Halfedge_handle to)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel;
+ typedef typename InexactKernel::Plane_3 InexactPlane_3;
+ typedef typename InexactKernel::Segment_3 InexactSegment_3;
+ typedef typename InexactKernel::Vector_3 InexactVector_3;
+ typedef typename InexactKernel::Point_3 InexactPoint_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+
+ std::vector<InexactSegment_3> segments;
+ Halfedge_handle current = from;
+ while (current != to)
+ {
+ const Point_3& s1 = current->vertex()->point();
+ const Point_3& s2 = current->next()->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(s1[0]),
+ CGAL::to_double(s1[1]),
+ CGAL::to_double(s1[2])),
+ InexactPoint_3(CGAL::to_double(s2[0]),
+ CGAL::to_double(s2[1]),
+ CGAL::to_double(s2[2]))));
+ current = current->next();
+ }
+
+ InexactPlane_3 fitting_plane;
+ CGAL::linear_least_squares_fitting_3(segments.begin(),
+ segments.end(),
+ fitting_plane,
+ CGAL::Dimension_tag<1>());
+
+ const InexactVector_3 orthogonal_vector = fitting_plane.orthogonal_vector()/sqrt(CGAL::to_double(fitting_plane.orthogonal_vector().squared_length()));
+
+ dolfin_assert(dolfin::near(orthogonal_vector.squared_length(), 1., DOLFIN_EPS_LARGE));
+
+ double max_distance = 0.;
+ double min_cos_angle = 1.;
+
+ current = from;;
+ while (current != to)
+ {
+ const Point_3& p1 = current->vertex()->point();
+ const InexactPoint_3 p1_inexact(CGAL::to_double(p1[0]),
+ CGAL::to_double(p1[1]),
+ CGAL::to_double(p1[2]));
+
+ const Point_3& next = current->next()->vertex()->point();
+ const InexactPoint_3 next_inexact(CGAL::to_double(next[0]),
+ CGAL::to_double(next[1]),
+ CGAL::to_double(next[2]));
+
+ const InexactVector_3 current_vector = (p1_inexact-next_inexact)/sqrt(CGAL::to_double((p1_inexact-next_inexact).squared_length()));
+ dolfin_assert(dolfin::near(current_vector.squared_length(), 1., DOLFIN_EPS_LARGE));
+
+ // Check if the angle between this vector and the fitting plane is acceptable
+ min_cos_angle = std::min(min_cos_angle,
+ CGAL::abs(current_vector*orthogonal_vector));
+
+ max_distance = std::max(max_distance,
+ sqrt((p1_inexact-fitting_plane.projection(p1_inexact)).squared_length()));
+
+ current = current->next();
+ }
+
+ return std::make_pair(max_distance, min_cos_angle);
+ }
+ //-----------------------------------------------------------------------------
+ template <typename Polyhedron>
+ static std::pair<typename Polyhedron::Halfedge_handle, typename Polyhedron::Halfedge_handle>
+ find_best_cut (Polyhedron& P,
+ const typename Polyhedron::Halfedge_handle h,
+ double cos_angle_tolerance)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel;
+ typedef typename InexactKernel::Plane_3 InexactPlane_3;
+ typedef typename InexactKernel::Point_3 InexactPoint_3;
+ typedef typename InexactKernel::Vector_3 InexactVector_3;
+ typedef typename InexactKernel::Segment_3 InexactSegment_3;
+
+ Halfedge_handle h1_distance, h2_distance;
+ double min_distance = std::numeric_limits<double>::max();
+
+ Halfedge_handle h1_angle, h2_angle;
+ double min_cos_normal = std::numeric_limits<double>::max();
+
+ Halfedge_handle current1 = h->next()->next();
+ const Halfedge_handle current1_end = h->prev()->prev();
+ do
+ {
+ // std::cout << "Current1 " << current1->vertex()->point() << std::endl;
+ Halfedge_handle current2 = current1->next()->next();
+ const Halfedge_handle current2_end = current1->prev();
+ do
+ {
+ /* std::cout << " Current 2" << std::endl; */
+ /* std::cout << "Segment " << current1->vertex()->point() << ", " << current2->vertex()->point() << std::endl; */
+
+ double max_distance_local = 0.;
+ double max_cos_normal_local = 0;
+
+ // Compute fitting plane of segments from current1 to current2
+ // std::cout << "Compute fit plane 1" << std::endl;
+ {
+ InexactPlane_3 fitting_plane;
+ std::vector<InexactSegment_3> segments;
+ Halfedge_handle current3 = current1;
+ while (current3 != current2)
+ {
+ const Point_3& s1 = current3->vertex()->point();
+ const Point_3& s2 = current3->next()->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(s1[0]),
+ CGAL::to_double(s1[1]),
+ CGAL::to_double(s1[2])),
+ InexactPoint_3(CGAL::to_double(s2[0]),
+ CGAL::to_double(s2[1]),
+ CGAL::to_double(s2[2]))));
+ current3 = current3->next();
+ }
+
+ /* const double fit_quality = */
+ CGAL::linear_least_squares_fitting_3(segments.begin(),
+ segments.end(),
+ fitting_plane,
+ CGAL::Dimension_tag<1>());
+ const InexactVector_3 orthogonal_vector = fitting_plane.orthogonal_vector()/sqrt(CGAL::to_double(fitting_plane.orthogonal_vector().squared_length()));
+
+ // std::cout << "Plane vector length: " << (CGAL::abs(orthogonal_vector.squared_length())-1) << std::endl;
+ dolfin_assert(dolfin::near(orthogonal_vector.squared_length(), 1., DOLFIN_EPS_LARGE));
+
+ current3 = current1;
+ while (current3 != current2)
+ {
+ const Point_3& p1 = current3->vertex()->point();
+ const InexactPoint_3 p1_inexact(CGAL::to_double(p1[0]),
+ CGAL::to_double(p1[1]),
+ CGAL::to_double(p1[2]));
+
+ const Point_3& next = current3->next()->vertex()->point();
+ const InexactPoint_3 next_inexact(CGAL::to_double(next[0]),
+ CGAL::to_double(next[1]),
+ CGAL::to_double(next[2]));
+
+ const InexactVector_3 current_vector = (p1_inexact-next_inexact)/sqrt(CGAL::to_double((p1_inexact-next_inexact).squared_length()));
+ dolfin_assert(dolfin::near(current_vector.squared_length(), 1., DOLFIN_EPS_LARGE));
+
+ // Check if angle between this vector and the fitting plane is acceptable
+ max_cos_normal_local = std::max(max_cos_normal_local, CGAL::abs(current_vector*orthogonal_vector));
+
+ max_distance_local = std::max(max_distance_local,
+ CGAL::to_double((p1_inexact-fitting_plane.projection(p1_inexact)).squared_length()));
+
+ current3 = current3->next();
+ }
+ }
+
+ // std::cout << "Done evaluating candidate: " << std::endl;
+
+ // Compute fitting plane of segments from current2 to current1
+ {
+ InexactPlane_3 fitting_plane;
+ std::vector<InexactSegment_3> segments;
+ Halfedge_handle current3 = current2;
+ while (current3 != current1)
+ {
+ const Point_3& s1 = current3->vertex()->point();
+ const Point_3& s2 = current3->next()->vertex()->point();
+ segments.push_back(InexactSegment_3(InexactPoint_3(CGAL::to_double(s1[0]),
+ CGAL::to_double(s1[1]),
+ CGAL::to_double(s1[2])),
+ InexactPoint_3(CGAL::to_double(s2[0]),
+ CGAL::to_double(s2[1]),
+ CGAL::to_double(s2[2]))));
+ current3 = current3->next();
+ }
+
+ /* const double fit_quality = */ CGAL::linear_least_squares_fitting_3(segments.begin(),
+ segments.end(),
+ fitting_plane,
+ CGAL::Dimension_tag<1>());
+
+ const InexactVector_3 orthogonal_vector = fitting_plane.orthogonal_vector()/sqrt(CGAL::to_double(fitting_plane.orthogonal_vector().squared_length()));
+ // const double angle_tolerance = .5;
+ // std::cout << "Plane vector length: " << (CGAL::abs(orthogonal_vector.squared_length())-1) << std::endl;
+ dolfin_assert(dolfin::near(CGAL::to_double(orthogonal_vector.squared_length()), 1., DOLFIN_EPS_LARGE));
+
+ current3 = current2;
+ while (current3 != current1)
+ {
+ const Point_3& p1 = current3->vertex()->point();
+ const InexactPoint_3 p1_inexact(CGAL::to_double(p1[0]),
+ CGAL::to_double(p1[1]),
+ CGAL::to_double(p1[2]));
+
+ const Point_3& next = current3->next()->vertex()->point();
+ const InexactPoint_3 next_inexact(CGAL::to_double(next[0]),
+ CGAL::to_double(next[1]),
+ CGAL::to_double(next[2]));
+
+ const InexactVector_3 current_vector = (p1_inexact-next_inexact)/sqrt(CGAL::to_double((p1_inexact-next_inexact).squared_length()));
+ dolfin_assert(dolfin::near(CGAL::to_double(current_vector.squared_length()), 1., DOLFIN_EPS_LARGE));
+
+ // Check if the angle between this vector and the fitting plane is acceptable
+ max_cos_normal_local = std::max(max_cos_normal_local, CGAL::abs(current_vector*orthogonal_vector));
+
+ max_distance_local = std::max(max_distance_local,
+ CGAL::to_double((p1_inexact-fitting_plane.projection(p1_inexact)).squared_length()));
+
+ current3 = current3->next();
+ }
+ }
+
+ // std::cout << "Done evaluating other half: " << std::endl;
+
+ if (max_distance_local < min_distance)
+ {
+ // std::cout << "New min split" << std::endl;
+ min_distance = max_distance_local;
+ h1_distance = current1;
+ h2_distance = current2;
+ }
+
+ if (max_cos_normal_local < min_cos_normal)
+ {
+ min_cos_normal = max_cos_normal_local;
+ h1_angle = current1;
+ h2_angle = current2;
+ }
+
+ current2 = current2->next();
+ } while (current2 != current2_end);
+
+ current1 = current1->next();
+ } while (current1 != current1_end);
+
+ std::cout << "Best cut, distance=" << min_distance << " : Segment " << h1_distance->vertex()->point() << ", " << h2_distance->vertex()->point() << std::endl;
+ std::cout << "Best cut, angle=" << min_cos_normal << " : Segment " << h1_angle->vertex()->point() << ", " << h2_angle->vertex()->point() << std::endl;
+
+
+ return std::make_pair(h1_angle, h2_angle);
+ }
+ //-----------------------------------------------------------------------------
+ template <typename Polyhedron>
+ static bool split_hole_planar(Polyhedron& P,
+ typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+
+ std::cout << "Split hole" << std::endl;
+
+ const std::pair<Halfedge_handle, Halfedge_handle> best_cut = find_best_cut(P, h, .3);
+
+ std::cout << "Found best cut" << std::endl;
+ if (best_cut.first == Halfedge_handle() || best_cut.second == Halfedge_handle())
+ return false;
+
+
+ const std::pair<double, double> planarity1 = evaluate_planarity<Polyhedron>(best_cut.first, best_cut.second);
+ const std::pair<double, double> planarity2 = evaluate_planarity<Polyhedron>(best_cut.second, best_cut.first);
+
+ std::cout << "Planarity: " << planarity1.first << " vs " << planarity2.first << std::endl;
+
+ const Halfedge_handle start = planarity1.first < planarity2.first ? best_cut.first : best_cut.second;
+ const Halfedge_handle end = planarity1.first < planarity2.first ? best_cut.second : best_cut.first;
+
+ // Compute average edge length
+ double length = 0.;
+ int num_edges = 0;
+ Halfedge_handle current = best_cut.first;
+ do
+ {
+ length += sqrt(CGAL::to_double((current->vertex()->point()-current->next()->vertex()->point()).squared_length()));
+ num_edges++;
+ } while (current != best_cut.first);
+
+ length /= num_edges;
+
+ const Vector_3 cut = start->vertex()->point() - end->vertex()->point();
+ const double cut_length = sqrt(CGAL::to_double(cut.squared_length()));
+ const int num_new_edges = static_cast<int>(cut_length/length);
+
+ P.fill_hole(h);
+
+ Halfedge_handle new_diagonal = P.split_facet(end, start);
+ // Now new_diagonal is on the facet to be
+
+ std::cout << "Inserting " << num_new_edges << " vertices from " << start->vertex()->point() << " to " << end->vertex()->point() << std::endl;
+ for (int i = 1; i < num_new_edges; i++)
+ {
+ Halfedge_handle hnew = P.split_edge(new_diagonal);
+ hnew->vertex()->point() = end->vertex()->point() + cut*i/num_new_edges;
+ // std::cout << " " << i << ": " << hnew->vertex()->point() << (hnew->vertex()->point()-hnew->opposite()->vertex()->point()).squared_length() << std::endl;
+ }
+
+ /* Halfedge_handle c = new_diagonal; */
+ /* do */
+ /* { */
+ /* std::cout << "Edge: " << c->vertex()->point() << " <--> " << c->opposite()->vertex()->point() << " : " << (c->vertex()->point()-c->opposite()->vertex()->point()).squared_length() << std::endl; */
+ /* c = c->next(); */
+ /* } while (c != new_diagonal); */
+
+
+ const Halfedge_handle opposite = new_diagonal->opposite();
+ P.make_hole(new_diagonal);
+ const bool success = triangulate_polygon_3d(P, new_diagonal);
+ //dolfin_assert(success);
+
+ P.make_hole(opposite);
+ if (!success)
+ return false;
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+
+ /* template <typename Polyhedron> */
+ /* void min_vertex_degree(const Polyhedron& p) */
+ /* { */
+ /* std::size_t min_degree = std::numeric_limits<std::size_t>::max(); */
+ /* std::size_t min_degree_non_border = min_degree; */
+
+ /* for (typename Polyhedron::Vertex_const_iterator vit = p.vertices_begin(); */
+ /* vit != p.vertices_end(); vit++) */
+ /* { */
+ /* min_degree = std::min(min_degree, vit->vertex_degree); */
+
+ /* } */
+
+ /* std::cout << "Min vertex_degree: " << min_degree << std::endl; */
+ /* } */
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void list_hole(const typename Polyhedron::Halfedge_handle h)
+ {
+ std::size_t counter = 0;
+ std::cout << "Polygon";
+
+ {
+ typename Polyhedron::Halfedge_handle current = h;
+ do
+ {
+ counter++;
+ current = current->next();
+ } while(current != h);
+ }
+
+ // if (counter < 250)
+ // {
+ typename Polyhedron::Halfedge_handle current = h;
+ do
+ {
+ std::cout << " " << current->vertex()->point() << ",";
+
+ current = current->next();
+ } while(current != h);
+ // }
+ std::cout << std::endl;
+
+ // std::cout << " size: " << counter << std::endl;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::string print_triangle(typename Polyhedron::Halfedge_handle h)
+ {
+ std::stringstream ss;
+ ss << "Triangle "
+ << h->prev()->vertex()->point() << ", "
+ << h->vertex()->point() << ", "
+ << h->next()->vertex()->point() << std::endl;
+ ss << "Area: " << triangle_area<Polyhedron>(h) << std::endl;
+ return ss.str();
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static double triangle_area(typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+
+ Triangle_3 t(h->prev()->vertex()->point(),
+ h->vertex()->point(),
+ h->next()->vertex()->point());
+
+ return CGAL::to_double(t.squared_area());
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static typename Polyhedron::Vertex_handle get_common_vertex(typename Polyhedron::Facet_handle f1,
+ typename Polyhedron::Facet_handle f2)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Vertex_handle Vertex_handle;
+
+ // Find common vertex
+ Halfedge_handle h1 = f1->halfedge();
+ Halfedge_handle current1 = h1;
+ do
+ {
+ Halfedge_handle h2 = f2->halfedge();
+ Halfedge_handle current2 = h2;
+ do
+ {
+ if (current2->vertex() == current1->vertex())
+ return current2->vertex();
+
+ current2 = current2->next();
+ } while (h2 != current2);
+
+ current1 = current1->next();
+ } while (h1 != current1);
+
+ return Vertex_handle();
+ }
+ //-----------------------------------------------------------------------------
+ template <typename Polyhedron>
+ static double facet_angle(typename Polyhedron::Halfedge_handle h)
+ {
+ dolfin_assert(h->is_border());
+ dolfin_assert(h->next()->is_border());
+
+ typedef typename Polyhedron::Traits::Line_3 Line_3;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+
+ const Line_3 l(h->prev()->vertex()->point(),
+ h->vertex()->point());
+
+ const Point_3& p11 = h->next()->vertex()->point();
+ const Point_3 p12 = l.projection(p11);
+ const Vector_3 v1(p12, p11);
+
+ dolfin_assert(h->opposite()->facet()->is_triangle());
+
+ const Point_3& p21 = h->opposite()->next()->vertex()->point();
+ const Point_3 p22 = l.projection(p21);
+ const Vector_3 v2(p22, p21);
+
+ return CGAL::to_double((v1*v2)/std::sqrt(CGAL::to_double(v1.squared_length()*v2.squared_length())));
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Segment_3>
+ static bool segment_intersects_triangle(const Segment_3& s,
+ const typename CGAL::Kernel_traits<Segment_3>::Kernel::Triangle_3& t)
+ {
+ typedef typename CGAL::Kernel_traits<Segment_3>::Kernel::Point_3 Point_3;
+
+ auto result = CGAL::intersection(s, t);
+ if (!result)
+ return false;
+
+ if (const Point_3* p = boost::get<Point_3>(&*result))
+ {
+ if (*p == t[0] || *p == t[1] || *p == t[2])
+ return false;
+ else
+ return true;
+ }
+ else if (const Segment_3* s_ = boost::get<Segment_3>(&*result))
+ {
+ if ( (s.source() == t[0] || s.source() == t[1] || s.source() == t[2]) &&
+ (s.target() == t[0] || s.target() == t[1] || s.target() == t[2]) )
+ return false;
+ else
+ return true;
+ }
+
+ dolfin_assert(false);
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ // Check if two triangles intersect.
+ // Neighbor triangles (share a vertice or an edge) do not intersect
+ // if t1 == t2 (geometrically), the triangles do not intersect
+ template<typename Triangle_3>
+ static bool triangles_intersect(const Triangle_3& t1, const Triangle_3& t2)
+ {
+ typedef typename CGAL::Kernel_traits<Triangle_3>::Kernel::Point_3 Point_3;
+ typedef typename CGAL::Kernel_traits<Triangle_3>::Kernel::Segment_3 Segment_3;
+
+ if ( (t1[0] == t2[0] || t1[0] == t2[1] || t1[0] == t2[2]) &&
+ (t1[1] == t2[0] || t1[1] == t2[1] || t1[1] == t2[2]) &&
+ (t1[2] == t2[0] || t1[2] == t2[1] || t1[2] == t2[2]))
+ return false;
+
+ auto result = CGAL::intersection(t1, t2);
+
+ if (!result)
+ return false;
+
+ if (const Point_3* p = boost::get<Point_3>(&*result))
+ {
+ if (t1[0] == t2[0] || t1[0] == t2[1] || t1[0] == t2[2] ||
+ t1[1] == t2[0] || t1[1] == t2[1] || t1[1] == t2[2] ||
+ t1[2] == t2[0] || t1[2] == t2[1] || t1[2] == t2[2])
+ return false;
+ else
+ return true;
+ }
+ else if (const Segment_3* s = boost::get<Segment_3>(&*result))
+ {
+ std::size_t common_vertices = 0;
+ if (t1[0] == t2[0] || t1[0] == t2[1] || t1[0] == t2[2])
+ common_vertices++;
+
+ if (t1[1] == t2[0] || t1[1] == t2[1] || t1[1] == t2[2])
+ common_vertices++;
+
+ if (t1[2] == t2[0] || t1[2] == t2[1] || t1[2] == t2[2])
+ common_vertices++;
+
+ if (common_vertices > 1)
+ return false;
+ else
+ return true;
+ }
+ else if (const Triangle_3* t = boost::get<Triangle_3>(&*result))
+ return true;
+ else if (const std::vector<Point_3>* v = boost::get<std::vector<Point_3> >(&*result))
+ return true;
+
+ dolfin_assert(false);
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Triangle_3>
+ static bool triangle_set_intersects(const std::vector<Triangle_3>& t)
+ {
+ for (std::size_t i = 0; i < t.size(); i++)
+ {
+ for (std::size_t j = i+1; j < t.size(); j++)
+ {
+ if (triangles_intersect<Triangle_3>(t[i], t[j]))
+ return true;
+ }
+ }
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Halfedge_handle>
+ static std::size_t vertex_count_halfedges(Halfedge_handle h)
+ {
+ std::size_t count = 0;
+ Halfedge_handle current = h;
+ do
+ {
+ ++count;
+ current = current->next()->opposite();
+ } while (current != h);
+
+ dolfin_assert(count > 0);
+ return count;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::size_t total_vertex_count_halfedges(const Polyhedron& P, std::map<typename Polyhedron::Vertex_const_handle, std::size_t>& m)
+ {
+ std::size_t total_count = 0;
+ for (typename Polyhedron::Vertex_const_iterator it = P.vertices_begin(); it != P.vertices_end(); it++)
+ {
+ const std::size_t c = vertex_count_halfedges(it->halfedge());
+ m[it] = c;
+ // std::cout << " Halfedge count: " << c << std::endl;
+ if (c == 0)
+ {int tmp; std::cin >> tmp; }
+ total_count += c;
+ }
+ return total_count;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool halfedge_is_in_polyhedron(const Polyhedron& P,
+ typename Polyhedron::Halfedge_const_handle h)
+ {
+ for (auto hit = P.halfedges_begin(); hit != P.halfedges_end(); hit++)
+ {
+ if (hit == h)
+ return true;
+ }
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool vertex_is_in_polyhedron(const Polyhedron& P,
+ typename Polyhedron::Vertex_const_handle v)
+ {
+ for (auto vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
+ {
+ if (vit == v)
+ return true;
+ }
+
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool check_vertex_consistency(const Polyhedron& P)
+ {
+ // std::cout << "Checking vertex consistency" << std::endl;
+ std::size_t counter = 0;
+
+ // Build a set of the list of vertices for faster lookup
+ std::set<typename Polyhedron::Vertex_const_handle> vertex_set;
+ for (auto vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
+ {
+ dolfin_assert(vertex_set.count(vit) == 0);
+ vertex_set.insert(vit);
+ }
+
+ std::deque<typename Polyhedron::Halfedge_const_handle> queue;
+ std::set<typename Polyhedron::Halfedge_const_handle> visited;
+
+ queue.push_back(P.halfedges_begin());
+ while (!queue.empty())
+ {
+ typename Polyhedron::Halfedge_const_handle current = queue.back();
+ queue.pop_back();
+ if (visited.count(current) == 0)
+ {
+ counter++;
+ typename Polyhedron::Halfedge_const_handle start = current;
+ visited.insert(current);
+
+ // Walk around the facet (or hole). Check halfedges and queue opposites
+ do
+ {
+ if (vertex_set.count(current->vertex()) == 0)
+ {
+ // std::cout << "Vertex not in vertex list: " << current->vertex()->point() << std::endl;
+ return false;
+ }
+
+ // TODO: Add the opposite check: All vertices should be reachable via halfedges
+ queue.push_back(current->opposite());
+ current = current->next();
+ } while(current != start);
+ }
+ }
+
+ // std::cout << " Checked " << counter << " halfedges" << std::endl;
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Triangle_3>
+ static bool triangle_set_intersect_triangle(Triangle_3 t,
+ const std::vector<Triangle_3>& triangle_set)
+ {
+ for (auto tit = triangle_set.begin(); tit != triangle_set.end(); tit++)
+ {
+ if (triangles_intersect(*tit, t))
+ return true;
+ }
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Segment_3>
+ static bool segment_intersects_triangle_set(const Segment_3& s,
+ const std::vector<typename CGAL::Kernel_traits<Segment_3>::Kernel::Triangle_3>& triangle_set)
+ {
+ for (auto tit = triangle_set.begin(); tit != triangle_set.end(); tit++)
+ {
+ if (segment_intersects_triangle(s, *tit))
+ return true;
+ }
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool center_vertex_triangulation(Polyhedron& P,
+ const typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ // check if the triangulation with a center vertex intersects any of the
+ // neighbor triangles
+ // std::cout << "Attempting center vertex triangulation" << std::endl;
+ dolfin_assert(P.is_valid(false, 0));
+ dolfin_assert(halfedge_is_in_polyhedron(P, h));
+ dolfin_assert(vertex_is_in_polyhedron(P, h->vertex()));
+
+ const std::array<double, 3> plane_fit = get_plane_fit<Polyhedron>(h, h->prev());
+ if (plane_fit[0] < .85)
+ {
+ // std::cout << " Rejected. Not sufficiently planar: " << plane_fit[0] << std::endl;
+ return false;
+ }
+
+ // Collect set of neighbor triangles and compute centroid
+ std::vector<Triangle_3> triangles;
+ Point_3 centroid = CGAL::ORIGIN;
+ std::size_t counter = 0;
+ {
+ Halfedge_handle current = h;
+ do
+ {
+ if (!current->opposite()->is_border() && current->opposite()->facet()->facet_degree() == 3)
+ {
+ triangles.push_back(get_facet_triangle<Polyhedron>(current->opposite()));
+ }
+ centroid = centroid + (current->vertex()->point()-CGAL::ORIGIN);
+ counter++;
+
+ current = current->next();
+ } while (current != h);
+ }
+ // std::cout << "Number of triangles: " << triangles.size() << std::endl;
+ centroid = CGAL::ORIGIN + (centroid-CGAL::ORIGIN)/counter;
+ // std::cout << "Centroid: " << centroid << std::endl;
+
+ Halfedge_handle current = h;
+ do
+ {
+ Triangle_3 current_triangle(current->vertex()->point(), current->next()->vertex()->point(), centroid);
+ for (auto tit = triangles.begin(); tit != triangles.end(); tit++)
+ {
+ if (triangles_intersect(current_triangle, *tit))
+ {
+ // std::cout << "No: Triangle " << current_triangle[0] << " " << current_triangle[1] << " " << current_triangle[2] << std::endl;
+ // std::cout << "Triangle " << (*tit)[0] << " " << (*tit)[1] << " " << (*tit)[2] << std::endl;
+ return false;
+ }
+ }
+
+ triangles.push_back(current_triangle);
+
+ current = current->next();
+ } while (current != h);
+
+ // std::cout << "Facet degree before center vertex: " << h->facet()->facet_degree() << std::endl;
+
+ P.normalize_border();
+ dolfin_assert(P.is_valid(false, 1));
+ dolfin_assert(!h->is_border_edge());
+ dolfin_assert(h->facet()->facet_degree() > 3);
+
+ Halfedge_handle center = P.create_center_vertex(h);
+ /* Halfedge_handle g = h->next()->next(); */
+ /* dolfin_assert(check_vertex_consistency(P)); */
+ /* dolfin_assert(halfedge_is_in_polyhedron(P, h)); */
+ /* dolfin_assert(vertex_is_in_polyhedron(P, h->vertex())); */
+ /* dolfin_assert(vertex_is_in_polyhedron(P, g->vertex())); */
+ /* std::cout << "Splitting: Segment " << h->vertex()->point() << ", " << g->vertex()->point() << std::endl; */
+ /* std::cout << "Vertex degrees: " << h->vertex()->vertex_degree() << ", " << g->vertex()->vertex_degree() << std::endl; */
+ /* std::cout << "My count: " << vertex_count_halfedges(h) << ", " << vertex_count_halfedges(g) << std::endl; */
+
+ /* std::cout << "Facet degree: " << h->facet()->facet_degree() << std::endl; */
+ /* std::cout << "Opposite facet degree: " << h->opposite()->facet()->facet_degree() << std::endl; */
+ /* std::cout << "Num halfedges: " << P.size_of_halfedges() << std::endl; */
+ /* std::map<typename Polyhedron::Vertex_const_handle, std::size_t> vertex_degrees; */
+ /* std::cout << "My total count: " << total_vertex_count_halfedges(P, vertex_degrees); */
+ /* std::cout << "Mapped: " << vertex_degrees.at(h->vertex()) << ", " << vertex_degrees.at(g->vertex()) << std::endl; */
+ /* std::cout << "Size of map: " << vertex_degrees.size() << std::endl; */
+ /* std::cout << "-- Splitting facet --" << std::endl; */
+
+ /* Halfedge_handle diagonal = P.split_facet(h, g); */
+
+ /* std::cout << "Vertex degrees: " << h->vertex()->vertex_degree() << ", " << g->vertex()->vertex_degree() << std::endl; */
+ /* std::cout << "My count: " << vertex_count_halfedges(h) << ", " << vertex_count_halfedges(g) << std::endl; */
+ /* std::map<typename Polyhedron::Vertex_const_handle, std::size_t> vertex_degrees_after; */
+ /* std::cout << "My total count: " << total_vertex_count_halfedges(P, vertex_degrees_after) << std::endl; */
+ /* std::cout << "Size of map: " << vertex_degrees_after.size() << std::endl; */
+ /* std::cout << "Num halfedges: " << P.size_of_halfedges() << std::endl; */
+ /* std::cout << "Mapped: " << vertex_degrees_after.at(h->vertex()) << ", " << vertex_degrees_after.at(g->vertex()) << std::endl; */
+ /* for (auto it = vertex_degrees_after.begin(); it != vertex_degrees_after.end(); ++it) */
+ /* { */
+ /* if (vertex_degrees.at(it->first) != it->second) */
+ /* { */
+ /* std::cout << "DIFF!!!" << vertex_degrees[it->first] << " " << it->second << std::endl; */
+ /* } */
+
+ /* if (vertex_degrees.at(it->first) == 0 || it->second == 0) */
+ /* { */
+ /* std::cout << "zero degree " << it->first->point() << std::endl; */
+ /* } */
+ /* } */
+ /* dolfin_assert(P.is_valid(false)); */
+ /* Halfedge_handle c = diagonal->vertex()->halfedge(); */
+ /* Halfedge_handle start = c; */
+ /* do */
+ /* { */
+ /* if (c->opposite()->vertex() == diagonal->opposite()->vertex()) */
+ /* std::cout << "Yes!!!" << std::endl; */
+ /* c = c->opposite()->next(); */
+ /* } while (c != start); */
+ /* dolfin_assert(P.is_valid(false, 0)); */
+ /* std::cout << "Splitting edge: Segment " << diagonal->opposite()->vertex()->point() << ", " << diagonal->vertex()->point() << std::endl; */
+ /* Halfedge_handle center = P.split_edge(diagonal); */
+ center->vertex()->point() = centroid;
+ dolfin_assert(P.is_valid(false, 0));
+
+ /* std::cout << "Splitting facet: " << diagonal->opposite()->vertex()->point() << ", " << diagonal->opposite()->prev()->prev()->vertex()->point() << std::endl; */
+ /* P.split_facet(diagonal->opposite(), diagonal->opposite()->prev()->prev()); */
+
+ /* do */
+ /* { */
+ /* dolfin_assert(P.is_valid(false, 0)); */
+ /* std::cout << "adding diagonal: " << diagonal->next()->vertex()->point() << ", " << center->vertex()->point() << std::endl; */
+ /* diagonal = P.split_facet(diagonal->next(), center); */
+ /* diagonal = diagonal->opposite(); */
+
+ /* } while (diagonal->next()->next() != center); */
+ /* std::cout << "Center vertex degree: " << center->vertex()->vertex_degree() << std::endl; */
+ /* //P.normalize_border(); */
+ /* { */
+ /* std::ofstream ofile("center-vertex.off"); */
+ /* ofile << P; */
+ /* } */
+
+ /* dolfin_assert(P.is_valid()); */
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void close_hole(Polyhedron& P,
+ typename Polyhedron::Halfedge_handle h)
+ {
+ //typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ dolfin_assert(P.is_valid(false, 0));
+ dolfin_assert(P.is_pure_triangle());
+ dolfin_assert(h->is_border());
+
+ P.fill_hole(h);
+ P.normalize_border();
+
+ dolfin_assert(h->facet()->facet_degree() > 2);
+
+ // Since the facet may be split, we push the facets to a fifo queue.
+ // Neighbor facets are now not guaranteed to be triangles.
+ std::deque<Halfedge_handle> queue;
+ queue.push_back(h);
+
+ while (!queue.empty())
+ {
+ // std::cout << "--- Popping facet from queue (" << queue.size() << ")" << std::endl;
+ const Halfedge_handle current = queue.front();
+ queue.pop_front();
+
+ // list_hole<Polyhedron>(current);
+
+ dolfin_assert(P.is_valid(false, 0));
+ dolfin_assert(halfedge_is_in_polyhedron(P, current));
+
+ if (current->facet()->facet_degree() == 3)
+ {
+ //P.fill_hole(h);
+ dolfin_assert(current->opposite()->facet()->facet_degree() != 3 ||
+ !triangles_intersect(get_facet_triangle<Polyhedron>(current),
+ get_facet_triangle<Polyhedron>(current->opposite())));
+ dolfin_assert(current->next()->opposite()->facet()->facet_degree() != 3 ||
+ !triangles_intersect(get_facet_triangle<Polyhedron>(current),
+ get_facet_triangle<Polyhedron>(current->next()->opposite())));
+ dolfin_assert(current->prev()->opposite()->facet()->facet_degree() != 3 ||
+ !triangles_intersect(get_facet_triangle<Polyhedron>(current),
+ get_facet_triangle<Polyhedron>(current->prev()->opposite())));
+ }
+ else
+ {
+ // std::cout << "Attempting to triangulate in 2D" << std::endl;
+ dolfin_assert(halfedge_is_in_polyhedron(P, current));
+ if (!triangulate_polygon_3d(P, current, false))
+ {
+ dolfin_assert(halfedge_is_in_polyhedron(P, current));
+
+ Halfedge_handle facet = subdivide_facet(P, current);
+
+ queue.push_back(facet->opposite());
+ queue.push_back(facet);
+ }
+ }
+ }
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static typename Polyhedron::Halfedge_handle
+ subdivide_facet(Polyhedron& P, typename Polyhedron::Halfedge_handle h)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::Segment_3 Segment_3;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+
+ // Search for segments that divide the hole, such that the dividing segment
+ // does not intersect triangles next to the hole and the facets on each side
+ // of the split is as planar as possible.
+
+ // Store all triangles around the hole and compute max edge length
+ std::vector<Triangle_3> border_triangles;
+ double max_squared_edge_length = 0;
+ {
+ Halfedge_handle current = h;
+ do
+ {
+ max_squared_edge_length = std::max(max_squared_edge_length,
+ CGAL::to_double(Segment_3(current->prev()->vertex()->point(),
+ current->vertex()->point()).squared_length()));
+ if (current->opposite()->facet()->facet_degree() == 3)
+ {
+ border_triangles.push_back(get_facet_triangle<Polyhedron>(current->opposite()));
+ }
+
+ current = current->next();
+ } while (current != h);
+ }
+
+ dolfin_assert(border_triangles.size() > 4);
+
+ // Search for the best dividing segment
+ double best_quality = -1000;
+ Halfedge_handle best_outer;
+ Halfedge_handle best_inner;
+
+ Halfedge_handle current_outer = h;
+
+ // FIXME: This loop should run to h->prev()->prev(), but need
+ // handle the specially
+ const Halfedge_handle outer_end = h->prev()->prev();
+ do
+ {
+ Halfedge_handle current_inner = current_outer->next()->next();
+ const Halfedge_handle inner_end = h;
+ do
+ {
+ if (current_inner->next() != current_outer &&
+ current_inner->prev() != current_outer)
+ {
+
+ Segment_3 current_segment(current_outer->vertex()->point(),
+ current_inner->vertex()->point());
+
+ // Check that this does not introduce an intersection
+ if (!segment_intersects_triangle_set(current_segment, border_triangles))
+ {
+ const double candidate_quality = evaluate_hole_subdivision<Polyhedron>(current_inner, current_outer);
+
+ if (candidate_quality > best_quality)
+ {
+ best_outer = current_outer;
+ best_inner = current_inner;
+ best_quality = candidate_quality;
+ }
+ }
+ }
+
+ current_inner = current_inner->next();
+ } while (current_inner != inner_end);
+
+ current_outer = current_outer->next();
+ } while (current_outer != outer_end);
+
+ dolfin_assert(best_outer != Halfedge_handle());
+ dolfin_assert(best_inner != Halfedge_handle());
+
+ // std::cout << "Found best subdivision: " << std::endl;
+
+ // list_hole<Polyhedron>(best_outer);
+ // std::cout << "Segment " << best_outer->vertex()->point()
+ // << ", " << best_inner->vertex()->point() << std::endl;
+ // std::cout << "Quality: " << best_quality << std::endl;
+
+ dolfin_assert(P.is_valid(false, 0));
+ const Halfedge_handle new_diagonal = P.split_facet(best_inner, best_outer);
+ const Point_3& p = new_diagonal->opposite()->vertex()->point();
+
+ const Vector_3 new_edge(new_diagonal->opposite()->vertex()->point(),
+ new_diagonal->vertex()->point());
+
+ const int num_segments = static_cast<int>(sqrt(CGAL::to_double(new_edge.squared_length())/max_squared_edge_length)+.5);
+
+ // Note: Don't use std::size_t as 0-1 becomes very large...
+ for (int i = 1; i < num_segments; i++)
+ {
+ //std::cout << "Splitting segment" << std::endl;
+ Halfedge_handle new_segment = P.split_edge(new_diagonal);
+ new_segment->vertex()->point() = p + static_cast<double>(i)/num_segments * new_edge;
+ }
+
+ P.normalize_border();
+ dolfin_assert(P.is_valid(false, 0));
+
+ return new_diagonal;
+ }
+ //-----------------------------------------------------------------------------
+ /* template<typename Polyhedron> */
+ /* static double evaluate_heuristic(const Polyhedron& P, */
+ /* typename Polyhedron::Halfedge_handle h, */
+ /* double plane_fit) */
+ /* { */
+ /* typedef typename Polyhedron::Traits::Triangle_3 Triangle_3; */
+ /* typedef typename Polyhedron::Traits::Vector_3 Vector_3; */
+ /* typedef CGAL::Exact_predicates_inexact_constructions_kernel InexactKernel; */
+ /* typedef typename InexactKernel::Plane_3 InexactPlane_3; */
+
+
+ /* // const double distance_to_plane_weight = 1.0; */
+ /* const double planarity_weight = 1.0; */
+ /* const double dihedral_weight = 1.0; */
+ /* const double ear_angle_weight = 1.0; */
+
+ /* // Compute the planarity of the points excluding the current point */
+ /* InexactPlane_3 p; */
+ /* const double plane_fit_quality = get_plane_fit<Polyhedron>(h->next(), */
+ /* h->prev(), */
+ /* &p); */
+ /* // Compute the maximum of the dihedral angle to the neighbors */
+ /* const Triangle_3 candidate_triangle(h->prev()->vertex()->point(), */
+ /* h->vertex()->point(), */
+ /* h->next()->vertex()->point()); */
+ /* const double cos_dihedral = (std::min(get_triangle_cos_angle(candidate_triangle, */
+ /* get_facet_triangle<Polyhedron>(h->opposite())), */
+ /* get_triangle_cos_angle(candidate_triangle, */
+ /* get_facet_triangle<Polyhedron>(h->next()->opposite())))+1)/2; */
+
+ /* // Compute the angle of the cutted ear */
+ /* const Vector_3 v1(h->vertex()->point(), */
+ /* h->prev()->vertex()->point()); */
+ /* const Vector_3 v2(h->vertex()->point(), */
+ /* h->next()->vertex()->point()); */
+
+ /* const double cos_ear_angle = (CGAL::to_double((v1*v2)/std::sqrt(CGAL::to_double(v1.squared_length()*v2.squared_length())))+1)/2.0; */
+ /* const double ear_angle_quality = cos_ear_angle; */
+
+ /* std::cout << "Triangle " << candidate_triangle[0] */
+ /* << ", " << candidate_triangle[1] */
+ /* << "," << candidate_triangle[2] << std::endl; */
+
+ /* std::cout << "Evaluate: planarity: " << (plane_fit_quality/plane_fit) */
+ /* << ", dihedral: " << cos_dihedral */
+ /* << ", ear angle: " << ear_angle_quality << std::endl; */
+
+ /* return planarity_weight*plane_fit_quality + dihedral_weight*cos_dihedral + ear_angle_quality*ear_angle_weight; */
+
+ /* /\* return planarity_weight*plane_fit_quality/plane_fit + *\/ */
+ /* /\* dihedral_weight*cos_dihedral + *\/ */
+ /* /\* (1-std::exp(-ear_angle_weight*cos_ear_angle))*cos_dihedral; *\/ */
+ /* } */
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool vertex_is_border(typename Polyhedron::Vertex_const_handle v)
+ {
+ //typename Polyhedron::Vertex::Halfedge_around_vertex_circulator h_start = v->vertex_begin();
+ auto h_start = v->vertex_begin();
+ auto h_current = h_start;
+ do
+ {
+ if (h_current->is_border_edge())
+ return true;
+ h_current++;
+ } while(h_current != h_start);
+
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool facets_are_neighbors(typename Polyhedron::Facet_handle f1,
+ typename Polyhedron::Facet_handle f2)
+ {
+ typename Polyhedron::Halfedge_handle h1 = f1->halfedge();
+ typename Polyhedron::Halfedge_handle start1 = h1;
+ do
+ {
+ typename Polyhedron::Halfedge_handle h2 = f2->halfedge();
+ typename Polyhedron::Halfedge_handle start2 = h2;
+ do
+ {
+ if (h2->vertex() == h1->vertex())
+ return true;
+
+ h2 = h2->next();
+ } while (h2 != start2);
+
+ h1 = h1->next();
+ } while (h1 != start1);
+
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static bool segment_intersects_facets(typename Polyhedron::Vertex_handle v1,
+ typename Polyhedron::Vertex_handle v2)
+ {
+ typedef typename Polyhedron::Halfedge_around_vertex_circulator Vertex_circulator;
+ typedef typename Polyhedron::Traits::Segment_3 Segment_3;
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+
+ Segment_3 s(v1->point(), v2->point());
+
+ Vertex_circulator start = v1->vertex_begin();
+ Vertex_circulator current = start;
+ do
+ {
+ if (!current->is_border() && current->facet()->is_triangle())
+ {
+ Triangle_3 t = get_facet_triangle<Polyhedron>(current);
+ auto result = CGAL::intersection(t, s);
+ dolfin_assert(result);
+
+ if (const Point_3* p = boost::get<Point_3>(&*result))
+ {
+ dolfin_assert(*p == s.source() || *p == s.target());
+ }
+ else if (const Segment_3* s = boost::get<Segment_3>(&*result))
+ {
+ return true;
+ }
+ else
+ {
+ dolfin_assert(false);
+ }
+ }
+
+ current++;
+ } while (start != current);
+
+ return false;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::string list_self_intersections(Polyhedron& p)
+ {
+ typedef typename Polyhedron::Facet_handle Facet_handle;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits Polyhedron_traits;
+ typedef typename Polyhedron_traits::Triangle_3 Triangle_3;
+ typedef typename Polyhedron_traits::Point_3 Point_3;
+ typedef typename Polyhedron_traits::Segment_3 Segment_3;
+
+ std::stringstream ss;
+
+ std::vector<std::pair<Facet_handle, Facet_handle> > intersections;
+ CGAL::Polygon_mesh_processing::self_intersections(p, std::back_inserter(intersections));
+
+ for (const std::pair<Facet_handle, Facet_handle>& iit : intersections)
+ {
+ ss << "Intersection (neighbors: " << (facets_are_neighbors<Polyhedron>(iit.first, iit.second) ? "Yes" : "No") << ")" << std::endl;
+ const Halfedge_handle h1 = iit.first->halfedge();
+ const Halfedge_handle h2 = iit.second->halfedge();
+ print_triangle<Polyhedron>(h1);
+ print_triangle<Polyhedron>(h2);
+
+ // Compute intersection
+
+ const Triangle_3 t1(h1->vertex()->point(),
+ h1->next()->vertex()->point(),
+ h1->next()->next()->vertex()->point());
+
+ const Triangle_3 t2(h2->vertex()->point(),
+ h2->next()->vertex()->point(),
+ h2->next()->next()->vertex()->point());
+
+ const auto result = CGAL::intersection(t1, t2);
+
+ dolfin_assert(result);
+ if (const Segment_3* s = boost::get<Segment_3>(&*result))
+ {
+ ss << "Segment: " << *s << std::endl;
+ }
+ else if (const Point_3* p = boost::get<Point_3>(&*result))
+ {
+ ss << "Point: " << *p << std::endl;
+ }
+ else if (const Triangle_3* t = boost::get<Triangle_3>(&*result))
+ {
+ ss << "Triangle: " << *t << std::endl;
+ }
+ else
+ {
+ ss << "Polygon" << std::endl;
+ }
+ }
+
+ return ss.str();
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static double closest_vertices(const Polyhedron& p)
+ {
+ // TODO: Use a better optimal algorithm for closest pair problem
+ std::cout << "Computing closest vertices" << std::endl;
+ double min_distance = std::numeric_limits<double>::max();
+
+ std::size_t counter = 0;
+ std::cout << "Vertices: " << p.size_of_vertices() << std::endl;
+ for (typename Polyhedron::Vertex_const_iterator v1 = p.vertices_begin();
+ v1 != p.vertices_end(); v1++)
+ {
+ if (counter % 1000 == 0)
+ std::cout << counter << std::endl;
+
+ typename Polyhedron::Vertex_const_handle v2 = v1;
+ v2++;
+ std::size_t counter2 = 0;
+ for (;v2 != p.vertices_end(); v2++)
+ {
+ min_distance = std::min(min_distance,
+ CGAL::to_double(CGAL::squared_distance(v1->point(), v2->point())));
+
+ if (min_distance == 0)
+ return 0.0;
+
+ counter2++;
+ }
+
+ counter++;
+ }
+
+ std::cout << " Done computing closest" << std::endl;
+ return min_distance;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::size_t min_vertex_degree(const Polyhedron& p)
+ {
+ std::size_t min_degree = std::numeric_limits<std::size_t>::max();
+ for (typename Polyhedron::Vertex_const_iterator it = p.vertices_begin();
+ it != p.vertices_end(); it++)
+ {
+ if (!vertex_is_border<Polyhedron>(it))
+ {
+ min_degree = std::min(min_degree, it->vertex_degree());
+ }
+ }
+ return min_degree;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void remove_vertex(Polyhedron& P, typename Polyhedron::Vertex_handle v)
+ {
+ typedef typename Polyhedron::Halfedge_around_vertex_circulator Vertex_circulator;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ // std::cout << "Removing vertex" << std::endl;
+
+ Vertex_circulator h = v->vertex_begin();
+ Vertex_circulator start = h;
+
+ std::vector<Halfedge_handle> to_be_removed;
+
+ do
+ {
+ if (!h->is_border())
+ to_be_removed.push_back(h);
+
+ h++;
+ } while (h != start);
+
+
+ // std::cout << "Removing " << to_be_removed.size() << " halfedges" << std::endl;
+ for (auto it = to_be_removed.begin(); it != to_be_removed.end(); it++)
+ {
+ P.erase_facet(*it);
+ }
+
+ // std::cout << " done removing vertex" << std::endl;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static std::size_t remove_self_intersections(Polyhedron& P)
+ {
+ std::size_t removed = 0;
+
+ // typedef typename Polyhedron::Traits Polyhedron_traits;
+ typedef typename Polyhedron::Facet_handle Facet_handle;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ std::vector<std::pair<Facet_handle, Facet_handle> > intersections;
+ CGAL::Polygon_mesh_processing::self_intersections(P, std::back_inserter(intersections));
+
+ while (intersections.size() > 0)
+ {
+ std::cout << "Removing self intersection (" << intersections.size() << ")" << std::endl;
+ const typename Polyhedron::Facet_handle f1 = intersections.front().first;
+ const typename Polyhedron::Facet_handle f2 = intersections.front().second;
+
+ std::deque<Facet_handle> queue1;
+ queue1.push_back(f1);
+
+ std::deque<Facet_handle> queue2;
+ queue2.push_back(f2);
+
+ std::set<Facet_handle> to_be_removed1;
+ to_be_removed1.insert(f1);
+
+ std::set<Facet_handle> to_be_removed2;
+ to_be_removed2.insert(f2);
+
+ bool f1_done = false;
+
+ while (!f1_done)
+ {
+ // Pop from queue 1
+ if (queue1.size() > 0)
+ {
+ to_be_removed1.insert(queue1.front());
+ Halfedge_handle start = queue1.front()->halfedge();
+
+ queue1.pop_front();
+ Halfedge_handle current = start;
+
+ do
+ {
+ std::cout << "Spreading out 1" << std::endl;
+ if (!current->is_border_edge())
+ {
+ //if (to_be_removed2.count(current->opposite()->facet()) > 0)
+ if (current->opposite()->facet() == f2)
+ {
+ f1_done = true;
+ break;
+ }
+ else
+ {
+ queue1.push_back(current->opposite()->facet());
+ }
+ }
+ current = current->next();
+ } while (current != start);
+ }
+ else
+ {
+ f1_done = true;
+ }
+ }
+
+ bool f2_done = false;
+
+ while (!f2_done)
+ {
+ // Pop from queue 2
+ if (queue2.size() > 0)
+ {
+ to_be_removed2.insert(queue2.front());
+ Halfedge_handle start = queue2.front()->halfedge();
+ queue2.pop_front();
+ Halfedge_handle current = start;
+
+ do
+ {
+ std::cout << "Spreading out 2" << std::endl;
+ if (!current->is_border_edge())
+ {
+ // if (to_be_removed1.count(current->opposite()->facet()) > 0)
+ if (current->opposite()->facet() == f1)
+ {
+ f2_done = true;
+ break;
+ }
+ else
+ {
+ queue2.push_back(current->opposite()->facet());
+ }
+ }
+ current = current->next();
+ } while (current != start);
+ }
+ else
+ {
+ f2_done = true;
+ }
+ }
+
+ std::cout << "To be removed 1: " << to_be_removed1.size() << std::endl;
+ std::cout << "To be removed 2: " << to_be_removed2.size() << std::endl;
+
+ to_be_removed1.insert(to_be_removed2.begin(),
+ to_be_removed2.end());
+
+ for (typename Polyhedron::Face_handle f : to_be_removed1)
+ {
+ P.erase_facet(f->halfedge());
+ dolfin_assert(P.is_valid());
+ removed++;
+ }
+
+
+ /* for (typename Polyhedron::Face_handle f : to_be_removed2) */
+ /* { */
+ /* P.erase_facet(f->halfedge()); */
+ /* dolfin_assert(P.is_valid()); */
+ /* removed++; */
+ /* } */
+
+ dolfin_assert(P.is_valid());
+
+ intersections.clear();
+ CGAL::Polygon_mesh_processing::self_intersections(P, std::back_inserter(intersections));
+
+ break;
+ }
+
+ return removed;
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void filter_sharp_features(Polyhedron& P, int start_facet, double tolerance)
+ {
+ typedef typename Polyhedron::Facet_iterator Facet_iterator;
+ typedef typename Polyhedron::Facet_handle Facet_handle;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle_3;
+
+ /* { */
+ /* std::ofstream outfile("before_filtering.off"); */
+ /* outfile << P; */
+ /* } */
+
+ /* std::cout << "Filter sharp features" << std::endl; */
+
+ const double cos_tolerance = std::cos(tolerance);
+ // std::cout << "tolerance: " << cos_tolerance << std::endl;
+ Facet_iterator fit = P.facets_begin();
+ for (int i = 0; i < start_facet; i++)
+ fit++;
+
+ std::deque<Facet_handle> queue;
+ {
+ Halfedge_handle h = fit->halfedge();
+ // std::cout << "Starting facet: Triangle " << h->vertex()->point() << ", ";
+ h = h->next();
+ // std::cout << h->vertex()->point() << ", ";
+ h = h->next();
+ // std::cout << h->vertex()->point() << std::endl;
+ }
+
+ std::set<Facet_handle> visited;
+ std::set<Facet_handle> to_be_removed;
+ for (Facet_iterator fit = P.facets_begin(); fit != P.facets_end(); fit++)
+ to_be_removed.insert(fit);
+
+ // std::cout << "Number of facets: " << P.size_of_facets() << std::endl;
+
+ queue.push_back(fit);
+ while (!queue.empty())
+ {
+ // std::cout << "In queue" << std::endl;
+ Facet_handle f = queue.front();
+ queue.pop_front();
+
+ if (visited.count(f) > 0)
+ {
+ // std::cout << "Already handled" << std::endl;
+ continue;
+ }
+
+ visited.insert(f);
+ to_be_removed.erase(f);
+
+ const Halfedge_handle start = f->halfedge();
+ Halfedge_handle current = start;
+ do
+ {
+ // std::cout << "Exploring neighbor" << std::endl;
+ if (!current->opposite()->is_border())
+ {
+
+ Triangle_3 t1 = get_facet_triangle<Polyhedron>(current);
+ Triangle_3 t2 = get_facet_triangle<Polyhedron>(current->opposite());
+ if (get_triangle_cos_angle(t1, t2) > cos_tolerance)
+ {
+ queue.push_back(current->opposite()->facet());
+ }
+ }
+
+ current = current->next();
+ } while (current != start);
+ }
+
+ // std::cout << "Remove " << to_be_removed.size() << " facets" << std::endl;
+
+ for (auto fit = to_be_removed.begin(); fit != to_be_removed.end(); fit++)
+ {
+ P.erase_facet( (*fit)->halfedge() );
+ }
+
+ /* { */
+ /* std::ofstream outfile("after_filtering.off"); */
+ /* outfile << P; */
+ /* } */
+ }
+
+//-----------------------------------------------------------------------------
+ template <typename Polyhedron>
+ static std::pair<typename Polyhedron::Traits::Point_3, typename Polyhedron::Traits::Point_3>
+ get_aabb(const Polyhedron& P)
+ {
+ typedef typename Polyhedron::Vertex_const_iterator Vertex_const_iterator;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::FT FT;
+ Vertex_const_iterator it = P.vertices_begin();
+
+ FT x_min = it->point().x();
+ FT y_min = it->point().y();;
+ FT z_min = it->point().z();
+ FT x_max = x_min;
+ FT y_max = y_min;
+ FT z_max = z_min;
+ it++;
+
+ for (;it != P.vertices_end(); it++)
+ {
+ const Point_3& current = it->point();
+ x_min = std::min(x_min, current.x());
+ y_min = std::min(y_min, current.y());
+ z_min = std::min(z_min, current.z());
+
+ x_max = std::max(x_max, current.x());
+ y_max = std::max(y_max, current.y());
+ z_max = std::max(z_max, current.z());
+ }
+
+ return std::make_pair(Point_3(x_min, y_min, z_min),
+ Point_3(x_max, y_max, z_max));
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static double sharpest_edge(const Polyhedron& P)
+ {
+ typedef typename Polyhedron::Traits Kernel;
+ typedef typename Polyhedron::Edge_const_iterator Edge_const_iterator;
+ typedef typename Kernel::Vector_3 Vector_3;
+
+ double min_cos = 1.0;
+ for (Edge_const_iterator it = P.edges_begin(); it != P.edges_end(); it++)
+ {
+ //const Point_3 a = it->vertex()->point();
+ const Vector_3 n1 = CGAL::normal<Kernel>(it->vertex()->point(),
+ it->next()->vertex()->point(),
+ it->next()->next()->vertex()->point());
+ const Vector_3 n2 = CGAL::normal<Kernel>(it->opposite()->vertex()->point(),
+ it->opposite()->next()->vertex()->point(),
+ it->opposite()->next()->next()->vertex()->point());
+ min_cos = std::min(min_cos,
+ CGAL::to_double(n1*n2)/sqrt(CGAL::to_double(n1.squared_length()*n2.squared_length())));
+ }
+
+ return acos(min_cos);
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void center_vertex_refinement(Polyhedron& P)
+ {
+ // Precollect facet handles since we are adding facets within the loop
+ std::vector<typename Polyhedron::Facet_handle> facets;
+ facets.reserve(P.size_of_facets());
+ for (typename Polyhedron::Facet_iterator fit = P.facets_begin();
+ fit != P.facets_end();
+ fit++)
+ {
+ facets.push_back(fit);
+ }
+
+ for (typename Polyhedron::Facet_handle f : facets)
+ {
+ typename Polyhedron::Halfedge_handle h = f->halfedge();
+ typename Polyhedron::Traits::Point_3 c = CGAL::centroid(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ c.exact();
+ typename Polyhedron::Halfedge_handle h_new = P.create_center_vertex(h);
+ h_new->vertex()->point() = c;
+ }
+ }
+ //-----------------------------------------------------------------------------
+ template<typename Polyhedron>
+ static void split_edge_refinement(Polyhedron& P)
+ {
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+
+ // Collect vertices from the original
+ std::set<Halfedge_handle> facets;
+ // facets.reserve(P.size_of_facets());
+ for (typename Polyhedron::Facet_iterator fit = P.facets_begin();
+ fit != P.facets_end();
+ fit++)
+ {
+ const Halfedge_handle h = fit->halfedge();
+ facets.insert(h);
+ }
+
+ // Split all edges
+ {
+ std::vector<Halfedge_handle> edges;
+ for (typename Polyhedron::Edge_iterator eit = P.edges_begin();
+ eit != P.edges_end(); eit++)
+ {
+ edges.push_back(eit);
+ }
+
+ for (Halfedge_handle h : edges)
+ {
+ const Point_3 p = CGAL::midpoint(h->vertex()->point(),
+ h->opposite()->vertex()->point());
+ Halfedge_handle opposite = h->opposite();
+ Halfedge_handle h_new = P.split_edge(h);
+ h_new->vertex()->point() = p;
+
+ // This is suboptimal, but split_edge(h) changes h->opposite() to point
+ // to the newly inserted vertex, so we have to check and update if
+ // h->opposite() is stored in facets
+ if (facets.erase(opposite))
+ {
+ facets.insert(h_new->opposite());
+ }
+ }
+ }
+
+ for (Halfedge_handle h : facets)
+ {
+ Halfedge_handle h2 = h->next()->next();
+ Halfedge_handle h3 = h2->next()->next();
+ P.split_facet(h->next(), h->prev());
+ P.split_facet(h2->next(), h2->prev());
+ P.split_facet(h3->next(), h3->prev());
+ }
+ }
+};
+ //-----------------------------------------------------------------------------
+// Taken from demo/Polyhedron/Scene_nef_polyhedron_item.cpp in the
+// CGAL source tree.
+// Quick hacks to convert polyhedra from exact to inexact and
+// vice-versa
+template <class Polyhedron_input, class Polyhedron_output>
+struct Copy_polyhedron_to
+ : public CGAL::Modifier_base<typename Polyhedron_output::HalfedgeDS>
+{
+ Copy_polyhedron_to(const Polyhedron_input& in_poly)
+ : _in_poly(in_poly) {}
+
+ void operator()(typename Polyhedron_output::HalfedgeDS& out_hds)
+ {
+ typedef typename Polyhedron_output::HalfedgeDS Output_HDS;
+ //typedef typename Polyhedron_input::HalfedgeDS Input_HDS;
+
+ CGAL::Polyhedron_incremental_builder_3<Output_HDS> builder(out_hds);
+
+ typedef typename Polyhedron_input::Vertex_const_iterator Vertex_const_iterator;
+ typedef typename Polyhedron_input::Facet_const_iterator Facet_const_iterator;
+ typedef typename Polyhedron_input::Halfedge_around_facet_const_circulator HFCC;
+
+ builder.begin_surface(_in_poly.size_of_vertices(),
+ _in_poly.size_of_facets(),
+ _in_poly.size_of_halfedges());
+
+ for(Vertex_const_iterator
+ vi = _in_poly.vertices_begin(), end = _in_poly.vertices_end();
+ vi != end ; ++vi)
+ {
+ typename Polyhedron_output::Point_3 p(::CGAL::to_double( vi->point().x()),
+ ::CGAL::to_double( vi->point().y()),
+ ::CGAL::to_double( vi->point().z()));
+ builder.add_vertex(p);
+ }
+
+ typedef CGAL::Inverse_index<Vertex_const_iterator> Index;
+ Index index(_in_poly.vertices_begin(), _in_poly.vertices_end());
+
+ for(Facet_const_iterator
+ fi = _in_poly.facets_begin(), end = _in_poly.facets_end();
+ fi != end; ++fi)
+ {
+ HFCC hc = fi->facet_begin();
+ HFCC hc_end = hc;
+ // std::size_t n = circulator_size( hc);
+ // CGAL_assertion( n >= 3);
+ builder.begin_facet ();
+ do
+ {
+ builder.add_vertex_to_facet(index[hc->vertex()]);
+ ++hc;
+ } while( hc != hc_end);
+ builder.end_facet();
+ }
+ builder.end_surface();
+ } // end operator()(..)
+private:
+ const Polyhedron_input& _in_poly;
+}; // end Copy_polyhedron_to<>
+
+template <class Poly_A, class Poly_B>
+void copy_to(const Poly_A& poly_a, Poly_B& poly_b)
+{
+ Copy_polyhedron_to<Poly_A, Poly_B> modifier(poly_a);
+ poly_b.delegate(modifier);
+ // CGAL_assertion(poly_b.is_valid());
+}
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/STLFileReader.h>
+#include "FuzzyPointLocator.h"
+
+#include <dolfin/geometry/Point.h>
+#include <dolfin/common/constants.h>
+#include <dolfin/log/LogStream.h>
+#include <dolfin/log/log.h>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+#include <boost/filesystem.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <algorithm>
+#include <string>
+#include <map>
+#include <math.h>
+namespace
+{
+inline double strToDouble(const std::string& s)
+{
+ std::istringstream is(s);
+ double val;
+ is >> val;
+
+ return val;
+}
+
+// get next line of file and trim away whitespace
+inline void get_next_line(std::ifstream& file, std::string& line, std::size_t &lineno)
+{
+ // Skip white
+ do
+ {
+ std::getline(file, line);
+ boost::algorithm::trim(line);
+ lineno++;
+ } while (file.good() && line.size() == 0);
+}
+
+} // end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+
+void STLFileReader::read(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets)
+{
+
+ dolfin::log(dolfin:: TRACE, "Reading surface from %s ", filename.c_str());
+
+ // vertices.clear();
+ facets.clear();
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+
+ std::ifstream file(filename.c_str());
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Failed to open file");
+ }
+
+ FuzzyPointMap vertex_map(1e-10);
+
+ std::string line;
+ std::size_t lineno = 0;
+ const boost::char_separator<char> sep(" ");
+
+ // Read the first line and trim away whitespaces
+ get_next_line(file, line, lineno);
+
+ if (line.substr(0, 5) != "solid")
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "File does not start with \"solid\" (line %u", lineno);
+ }
+
+ // TODO: Read name of solid
+
+ do
+ {
+ // Some files contain color information before the vertex information
+ get_next_line(file, line, lineno);
+ } while (line.substr(0, 5) != "facet");
+
+ while (file.good())
+ {
+ bool has_normal = false;
+ dolfin::Point normal;
+
+ // Read the line "facet normal n1 n2 n3"
+ {
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ if (*tok_iter != "facet")
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword \"facet\" (line %u)", lineno);
+ ++tok_iter;
+
+ // Check if a normal different from zero is given
+ if (tok_iter != tokens.end())
+ {
+ if (*tok_iter != "normal")
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword \"normal\"(line %u)", lineno);
+ ++tok_iter;
+
+ //dolfin::cout << "Read line: " << line << dolfin::endl;
+
+ for (std::size_t i = 0; i < 3; ++i)
+ {
+ normal[i] = strToDouble(*tok_iter);
+ ++tok_iter;
+ }
+
+ if (normal.norm() > DOLFIN_EPS)
+ has_normal = true;
+
+ if (tok_iter != tokens.end())
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected end of line (line %u)", lineno);
+ }
+ }
+
+ // if (has_normal)
+ // {
+ // dolfin::cout << "Has normal" << dolfin::endl;
+ // dolfin::cout << normal << dolfin::endl;
+ // }
+
+ // Read "outer loop" line
+ get_next_line(file, line, lineno);
+
+ if (line != "outer loop")
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword 'outer loop' (line %u)", lineno);
+
+ std::array<std::size_t, 3> v_indices;
+
+ get_next_line(file, line, lineno);
+
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ if (*tok_iter != "vertex")
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword vertex (line %u)", lineno);
+ }
+
+ int counter = 0;
+
+ // Read lines with vertices
+ do
+ {
+ // Only support for triangulated surfaces for now
+ dolfin_assert(counter < 3);
+
+ // Advance to next
+ ++tok_iter;
+
+ const double x = strToDouble(*tok_iter); ++tok_iter;
+ const double y = strToDouble(*tok_iter); ++tok_iter;
+ const double z = strToDouble(*tok_iter); ++tok_iter;
+
+ const std::array<double, 3> vertex = {{x, y, z}};
+
+ // Insert point and get index. vertex_map takes care of merging close
+ // vertices
+ v_indices[counter] = vertex_map.insert_point(vertex);
+
+ // Get next line
+ get_next_line(file, line, lineno);
+
+ tokens = tokenizer(line, sep);
+ tok_iter = tokens.begin();
+
+ counter++;
+ } while (*tok_iter == "vertex");
+
+ // Read 'endloop' line
+ if (line != "endloop")
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword endloop (line %u)", lineno);
+ }
+
+ get_next_line(file, line, lineno);
+ if (line != "endfacet")
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword endfacet (line %u)", lineno);
+ }
+
+ // Add facet to output
+ facets.push_back(v_indices);
+
+ // Get orientation right if normal is given
+ if (has_normal)
+ {
+ // Compute normal
+ const dolfin::Point v1(3, vertex_map[v_indices[0]].data());
+ const dolfin::Point v2(3, vertex_map[v_indices[1]].data());
+ const dolfin::Point v3(3, vertex_map[v_indices[2]].data());
+
+ const dolfin::Point a = v2-v1;
+ const dolfin::Point b = v3-v1;
+
+ dolfin::Point n = a.cross(b);
+ n /= n.norm();
+
+ // dolfin::cout << "Normal: " << n << dolfin::endl;
+ // if ( (n - normal).norm() > 1e-5 )
+ // dolfin::cout << "Diff: " << (n - normal).norm() << dolfin::endl;
+ }
+
+ // Get next line
+ // either start of next facet or endsolid
+ get_next_line(file, line, lineno);
+
+ if (line.substr(0, 5) != "facet")
+ break;
+ }
+
+ // Read the 'endsolid' line
+ tokenizer tokens(line, sep);
+ tokenizer::iterator tok_iter = tokens.begin();
+
+ if (*tok_iter != "endsolid")
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open .stl file to read 3D surface",
+ "Expected keyword endsolid at line %u", lineno);
+ }
+ ++tok_iter;
+
+ vertices = vertex_map.get_points();
+
+ // TODO: Check name of solid
+ dolfin::log(dolfin::TRACE, "Done reading surface");
+
+ // closest_vertices(vertex_map);
+}
+void STLFileReader::write(const std::string filename,
+ std::vector<std::array<double, 3> >& vertices,
+ std::vector<std::array<std::size_t, 3> >& facets)
+{
+
+ std::ofstream file(filename);
+ file.precision(6);
+
+ if (!file.is_open())
+ {
+ dolfin::dolfin_error("STLFileReader.cpp",
+ "open file to write stl data",
+ "Failed to open file");
+ }
+
+ file << "solid "<< filename << std::endl;
+
+
+
+
+ for (const std::array<std::size_t, 3>& f : facets)
+ {
+
+ std::array<double,3> n;
+ float vec1x,vec1y,vec1z;
+ float vec2x,vec2y,vec2z;
+ float size;
+
+ std::array<double, 3>& v0 = vertices[f[0]];
+ std::array<double, 3>& v1 = vertices[f[1]];
+ std::array<double, 3>& v2 = vertices[f[2]];
+
+ vec1x = v1[0] - v0[0];
+ vec1y = v1[1] - v0[1];
+ vec1z = v1[2] - v0[2];
+
+ vec2x = v2[0] - v0[0];
+ vec2y = v2[1] - v0[1];
+ vec2z = v2[2] - v0[2];
+
+ n[0] = vec1y*vec2z-vec1z*vec2y;
+ n[1] = vec1z*vec2x-vec1x*vec2z;
+ n[2] = vec1x*vec2y-vec1y*vec2x;
+ size = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
+
+ file << "facet normal " << n[0]/size << " " << n[1]/size << " " << n[2]/size <<std::endl;
+ file << "outer loop"<< std::endl;
+ file << "\t" << "vertex " << v0[0] << " " << v0[1]<< " " << v0[2] << std::endl;
+ file << "\t" << "vertex " << v1[0] << " " << v1[1]<< " " << v1[2] << std::endl;
+ file << "\t" << "vertex " << v2[0] << " " << v2[1]<< " " << v2[2] << std::endl;
+ file <<"endloop" << std::endl;
+ file <<"endfacet"<< std::endl;
+ }
+ file << "endsolid" << std::endl;
+
+}
+
+}
--- /dev/null
+// Copyright (C) 2014-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+// OBS! Experimental code
+
+#include <mshr/SurfaceConsistency.h>
+#include "FuzzyPointLocator.h"
+
+#include <dolfin/log/log.h>
+#include <vector>
+#include <map>
+#include <deque>
+#include <iostream>
+#include <limits>
+
+// #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+// typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
+// typedef Kernel::Point_3 Point_3;
+// typedef Kernel::Triangle_3 Triangle_3;
+
+// namespace
+// {
+// inline bool triangles_intersect(const std::vector<std::array<double, 3> >& vertices,
+// const std::vector<std::array<std::size_t, 3> >& facets,
+// std::size_t a,
+// std::size_t b)
+// {
+// const std::array<std::size_t, 3>& ta = facets[a];
+// const std::array<std::size_t, 3>& tb = facets[b];
+
+// std::cout << " Intersection test: " << std::endl;
+// std::cout << " Facet " << a << ": (" << ta[0] << ", " << ta[1] << ", " << ta[2] << ")" << std::endl;
+// std::cout << " Facet " << b << ": (" << tb[0] << ", " << tb[1] << ", " << tb[2] << ")" << std::endl;
+
+// for (std::size_t i = 0; i < 3; i++)
+// {
+// for (std::size_t j = 0; j < 3; j++)
+// {
+// if (ta[i] == tb[j] || (ta[i] == tb[(j+1)%3] && ta[(i+1)%3] == tb[j]))
+// {
+// std::cout << " Neighbor" << std::endl;
+// return false;
+// }
+// }
+// }
+
+// Triangle_3 t1(Point_3(vertices[ta[0]][0], vertices[ta[0]][1], vertices[ta[0]][2]),
+// Point_3(vertices[ta[1]][0], vertices[ta[1]][1], vertices[ta[1]][2]),
+// Point_3(vertices[ta[2]][0], vertices[ta[2]][1], vertices[ta[2]][2]));
+
+// Triangle_3 t2(Point_3(vertices[tb[0]][0], vertices[tb[0]][1], vertices[tb[0]][2]),
+// Point_3(vertices[tb[1]][0], vertices[tb[1]][1], vertices[tb[1]][2]),
+// Point_3(vertices[tb[2]][0], vertices[tb[2]][1], vertices[tb[2]][2]));
+
+// const bool i = CGAL::do_intersect(t1, t2);
+// std::cout << " Result: " << (i ? "True" : "False") << std::endl;
+// return i;
+// }
+// }
+
+namespace mshr
+{
+
+void SurfaceConsistency::checkConnectivity(std::vector<std::array<std::size_t, 3> >& facets,
+ std::set<std::size_t>& duplicating,
+ bool error)
+{
+ log(dolfin::TRACE, "Checking connectivity");
+
+ // Store halfedges
+ std::map<std::pair<std::size_t, std::size_t>, std::size_t> halfedges;
+
+ for (std::size_t facet_no = 0; facet_no < facets.size(); facet_no++)
+ {
+ std::array<std::size_t, 3>& f = facets[facet_no];
+ // Check for (topologically) degenerate facets
+ if ( f[0] == f[1] || f[0] == f[2] || f[1] == f[2] )
+ dolfin::dolfin_error("SurfaceConsistency.cpp",
+ "confirm surface connectivity",
+ "Facet %d is topologically degenerate", facet_no);
+
+ if (halfedges.count(std::make_pair(f[0], f[1])) > 0 ||
+ halfedges.count(std::make_pair(f[1], f[2])) > 0 ||
+ halfedges.count(std::make_pair(f[2], f[0])) > 0)
+ {
+ duplicating.insert(facet_no);
+ }
+ else
+ {
+ halfedges[std::make_pair(f[0], f[1])] = facet_no;
+ halfedges[std::make_pair(f[1], f[2])] = facet_no;
+ halfedges[std::make_pair(f[2], f[0])] = facet_no;
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+void SurfaceConsistency::filterFacets(const std::vector<std::array<std::size_t, 3> >& facets,
+ const std::vector<std::array<double, 3> >& vertices,
+ std::size_t start, std::set<std::size_t>& skip)
+{
+ // Map egdes (ordered pairs of vertices) to facets
+ std::map<std::pair<std::size_t, std::size_t>, std::size_t> edge_map;
+
+ for (std::size_t i = 0; i < facets.size(); i++)
+ {
+ const std::array<std::size_t, 3>& facet = facets[i];
+ std::size_t prev = facet[facet.size()-1];
+ for (auto vit = facet.begin(); vit != facet.end(); vit++)
+ {
+ const std::pair<std::size_t, std::size_t> e(prev, *vit);
+ edge_map[e] = i;
+ prev = *vit;
+ }
+
+ skip.insert(i);
+ }
+
+ // const std::size_t global_max = skip.size();
+
+ std::set<std::size_t> visited;
+ //std::vector<std::size_t> included;
+ std::deque<std::size_t> queue;
+ if (skip.count(start) > 0)
+ queue.push_front(start);
+ // else
+ // std::cout << " Already added" << std::endl;
+
+ std::size_t global_count = 0;
+ while (!queue.empty())
+ {
+ // dolfin_assert(global_count <= global_max);
+
+ std::size_t current = queue.front();
+ queue.pop_front();
+
+ const std::array<size_t, 3>& current_facet = facets[current];
+ // std::cout << "-- Processing " << current << ", vertices: " << current_facet[0] << ", " << current_facet[1] << ", " << current_facet[2] << std::endl;
+
+ visited.insert(current);
+
+ // don't skip this facet
+ if (skip.count(current) > 0)
+ {
+ // bool intersects = false;
+ // for (auto it = included.begin(); it != included.end(); it++)
+ // {
+ // if (triangles_intersect(vertices, facets, current, *it))
+ // {
+ // intersects = true;
+ // break;
+ // }
+ // }
+
+ // if (intersects)
+ // {
+ // std::cout << " SKIPPING" << std::endl;
+ // {int tmp; std::cin >> tmp;}
+ // continue;
+ // }
+
+ skip.erase(current);
+ //included.push_back(current);
+
+ std::size_t prev = current_facet[2];
+ for (auto fit = current_facet.begin(); fit != current_facet.end(); fit++)
+ {
+ const std::pair<std::size_t, std::size_t> opposite(*fit, prev);
+ // std::cout << " opposite: " << opposite.first << " " << opposite.second << std::endl;
+ if (edge_map.count(opposite) > 0)
+ {
+ std::size_t opposite_facet = edge_map[opposite];
+ if (visited.count(opposite_facet) == 0)
+ {
+ queue.push_back(opposite_facet);
+ //std::cout << " pushing: " << edge_map[opposite] << std::endl;
+ }
+ }
+ prev = *fit;
+ }
+
+ // std::cout << "Size of skip: " << skip.size() << ", size of queue: " << queue.size() << ", current: " << current << std::endl;
+ // {int tmp; std::cin >> tmp;}
+ global_count++;
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+std::pair<std::unique_ptr<std::vector<std::array<double, 3> > >,
+ std::unique_ptr<std::vector<std::array<std::size_t, 3> > > >
+SurfaceConsistency::merge_close_vertices(const std::vector<std::array<std::size_t, 3> >& facets,
+ const std::vector<std::array<double, 3> >& vertices,
+ double tolerance)
+{
+ FuzzyPointMap point_map(tolerance);
+ std::vector<std::size_t> vertex_mapping;
+ vertex_mapping.reserve(vertices.size());
+
+
+ for (std::size_t i = 0; i < vertices.size(); i++)
+ {
+ const std::array<double, 3>& v = vertices[i];
+ const std::size_t new_index = point_map.insert_point(v);
+ vertex_mapping.push_back(new_index);
+ }
+
+ // std::cout << "Distinct vertices: " << point_map.size() << std::endl;
+
+ std::unique_ptr<std::vector<std::array<double, 3> > > new_vertices(new std::vector<std::array<double, 3> >(point_map.get_points()));
+ std::unique_ptr<std::vector<std::array<std::size_t, 3> > > new_facets(new std::vector<std::array<std::size_t, 3> >);
+ for (const std::array<std::size_t, 3>& t : facets)
+ {
+ new_facets->push_back({ {vertex_mapping[t[0]], vertex_mapping[t[1]], vertex_mapping[t[2]]} });
+ }
+
+ return std::make_pair(std::move(new_vertices), std::move(new_facets));
+}
+//-----------------------------------------------------------------------------
+void SurfaceConsistency::orient_component(std::vector<std::array<std::size_t, 3> >& facets,
+ std::size_t start)
+{
+ log(dolfin::TRACE, "Checking facet orientation");
+
+ // Map from edge (pair of vertices) to two triangles
+ std::map<std::pair<std::size_t, std::size_t>, std::pair<std::size_t, std::size_t> > edge_map;
+ for (std::size_t i = 0; i < facets.size(); i++)
+ {
+ const std::array<std::size_t, 3>& f = facets[i];
+ for (std::size_t j = 0; j < 3; j++)
+ {
+ auto edge = std::make_pair(std::min(f[j], f[(j+1)%3]), std::max(f[j], f[(j+1)%3]));
+ if (edge_map.count(edge) > 0)
+ edge_map[edge].second = i;
+ else
+ edge_map[edge] = std::make_pair(i, std::numeric_limits<std::size_t>::max());
+ }
+ }
+
+ std::deque<std::size_t> queue;
+ std::set<std::size_t> visited;
+ queue.push_back(start);
+
+ std::size_t counter = 0;
+ std::size_t flipped = 0;
+ while (!queue.empty())
+ {
+ std::size_t current = queue.front();
+ const std::array<std::size_t, 3>& current_facet = facets[current];
+ queue.pop_front();
+
+ if (visited.count(current) == 0)
+ {
+ counter++;
+ visited.insert(current);
+
+ for (std::size_t j = 0; j < 3; j++)
+ {
+ auto edge = std::make_pair(std::min(current_facet[j], current_facet[(j+1)%3]), std::max(current_facet[j], current_facet[(j+1)%3]));
+ dolfin_assert(edge_map.count(edge) > 0);
+
+ auto facet_pair = edge_map[edge];
+ const std::size_t opposite = facet_pair.first == current ? facet_pair.second : facet_pair.first;
+
+ if (opposite != std::numeric_limits<std::size_t>::max() && visited.count(opposite) == 0)
+ {
+ dolfin_assert(opposite != current);
+
+ std::array<std::size_t, 3>& opposite_facet = facets[opposite];
+ for (std::size_t k = 0; k < 3; k++)
+ {
+ const std::size_t a = opposite_facet[k];
+ const std::size_t b = opposite_facet[(k+1)%3];
+ if ( (a == current_facet[0] && b == current_facet[1]) ||
+ (a == current_facet[1] && b == current_facet[2]) ||
+ (a == current_facet[2] && b == current_facet[0]))
+ {
+ flipped++;
+ std::swap(opposite_facet[0], opposite_facet[1]);
+ break;
+ }
+ }
+ queue.push_back(opposite);
+ }
+ }
+ }
+ }
+ log(dolfin::TRACE, "Flipped %u triangles", flipped);
+}
+
+std::size_t SurfaceConsistency::remove_null_facets(std::vector<std::array<std::size_t, 3>>& facets)
+{
+ std::size_t counter = 0;
+
+ std::vector<std::array<std::size_t, 3>>::iterator it = facets.begin();
+
+ while (it != facets.end())
+ {
+ const std::array<std::size_t, 3>& f = *it;
+ if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
+ {
+ *it = facets.back();
+ facets.pop_back();
+ counter++;
+ }
+ else
+ it++;
+ }
+
+ return counter;
+}
+
+std::size_t SurfaceConsistency::remove_isolated_vertices(std::vector<std::array<double, 3>>& vertices,
+ std::vector<std::array<std::size_t, 3>>& facets)
+{
+ std::size_t counter = 0;
+ std::vector<bool> is_connected(vertices.size());
+ std::fill(is_connected.begin(), is_connected.end(), false);
+ for (const std::array<std::size_t, 3>& f : facets)
+ {
+ is_connected[f[0]] = true;
+ is_connected[f[1]] = true;
+ is_connected[f[2]] = true;
+ }
+
+ for (std::size_t i_ = 0; i_ < is_connected.size(); i_++)
+ {
+ // Need to do this in reverse order, so the vertices with higher index is
+ // processed first to avoid invalidating the
+ const std::size_t i = is_connected.size()-i_-1;
+
+ if (!is_connected[i])
+ {
+ vertices.erase(vertices.begin()+i);
+
+ for (std::array<std::size_t, 3>& f : facets)
+ {
+ if (f[0] > i) f[0]--;
+ if (f[1] > i) f[1]--;
+ if (f[2] > i) f[2]--;
+ }
+
+ counter++;
+ }
+ }
+
+ return counter;
+}
+
+
+}
--- /dev/null
+// Copyright (C) 2015-2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/SurfaceReconstruction.h>
+#include <dolfin/log/log.h>
+#include <dolfin/common/constants.h>
+
+#define CGAL_EIGEN3_ENABLED true
+#include <CGAL/trace.h>
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Surface_mesh_default_triangulation_3.h>
+#include <CGAL/make_surface_mesh.h>
+#include <CGAL/Implicit_surface_3.h>
+#include <CGAL/Poisson_reconstruction_function.h>
+#include <CGAL/Point_with_normal_3.h>
+#include <CGAL/property_map.h>
+#include <CGAL/compute_average_spacing.h>
+
+#include <CGAL/Surface_mesh.h>
+#include <CGAL/Polygon_mesh_processing/remesh.h>
+
+// Based on CGAL/Surface_reconstruction_points_3/poisson_reconstruction_example.cpp
+typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
+typedef Kernel::FT FT;
+typedef Kernel::Point_3 Point;
+typedef Kernel::Vector_3 Vector;
+typedef CGAL::Point_with_normal_3<Kernel> Point_with_normal;
+typedef Kernel::Sphere_3 Sphere;
+typedef std::vector<Point_with_normal> PointList;
+typedef CGAL::Poisson_reconstruction_function<Kernel> Poisson_reconstruction_function;
+typedef CGAL::Surface_mesh_default_triangulation_3 STr;
+typedef CGAL::Surface_mesh_complex_2_in_triangulation_3<STr> C2t3;
+typedef CGAL::Implicit_surface_3<Kernel, Poisson_reconstruction_function> Surface_3;
+typedef typename C2t3::Triangulation Tr;
+typedef typename Tr::Vertex_handle Vertex_handle;
+typedef typename Tr::Edge Edge;
+typedef typename Tr::Facet Facet;
+typedef typename Tr::Finite_facets_iterator Finite_facets_iterator;
+
+
+namespace
+{
+
+template <class Vertex_handle>
+std::size_t get_vertex_index(std::vector<std::array<double, 3>>& vertices,
+ Vertex_handle vh,
+ std::map<Vertex_handle, std::size_t>& V,
+ std::size_t& inum)
+{
+ typedef typename std::map<Vertex_handle, std::size_t>::iterator map_iterator;
+ std::pair<map_iterator,bool> insert_res = V.insert( std::make_pair(vh,inum) );
+ if ( insert_res.second )
+ {
+ typename Tr::Point p = static_cast<typename Tr::Point>(vh->point());
+ vertices.push_back({ {CGAL::to_double(p[0]),
+ CGAL::to_double(p[1]),
+ CGAL::to_double(p[2])} });
+ ++inum;
+ }
+ return insert_res.first->second;
+}
+//-----------------------------------------------------------------------------
+void export_triangulation(const C2t3& c2t3,
+ std::vector<std::array<double, 3>>& vertices,
+ std::vector<std::array<std::size_t, 3>>& facets)
+{
+ const Tr& tr = c2t3.triangulation();
+ const typename Tr::size_type number_of_facets = c2t3.number_of_facets();
+
+ vertices.reserve(tr.number_of_vertices());
+ facets.reserve(number_of_facets);
+
+ // Finite vertices coordinates.
+ Finite_facets_iterator fit = tr.finite_facets_begin();
+ std::set<Facet> oriented_set;
+ std::stack<Facet> stack;
+
+ //CGAL_assertion_code(typename Tr::size_type nb_facets = 0; )
+
+ while (oriented_set.size() != number_of_facets)
+ {
+ while ( fit->first->is_facet_on_surface(fit->second) == false ||
+ oriented_set.find(*fit) != oriented_set.end() ||
+ oriented_set.find(c2t3.opposite_facet(*fit)) != oriented_set.end() )
+ {
+ ++fit;
+ }
+ oriented_set.insert(*fit);
+ stack.push(*fit);
+ while(! stack.empty() )
+ {
+ Facet f = stack.top();
+ stack.pop();
+ for(int ih = 0 ; ih < 3 ; ++ih)
+ {
+ const int i1 = tr.vertex_triple_index(f.second, tr. cw(ih));
+ const int i2 = tr.vertex_triple_index(f.second, tr.ccw(ih));
+ if( c2t3.face_status(Edge(f.first, i1, i2)) == C2t3::REGULAR )
+ {
+ Facet fn = c2t3.neighbor(f, ih);
+ if (oriented_set.find(fn) == oriented_set.end() &&
+ oriented_set.find(c2t3.opposite_facet(fn)) == oriented_set.end())
+ {
+ oriented_set.insert(fn);
+ stack.push(fn);
+ }
+ } // end "if the edge is regular"
+ } // end "for each neighbor of f"
+ } // end "stack non empty"
+ } // end "oriented_set not full"
+
+ // Orients the whole mesh towards outside:
+ // - find the facet with max z
+ typename std::set<Facet>::const_iterator top_facet = oriented_set.begin();
+ for(typename std::set<Facet>::const_iterator fit = oriented_set.begin();
+ fit != oriented_set.end();
+ ++fit)
+ {
+ double top_z =
+ (top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 0))->point().z()
+ + top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 1))->point().z()
+ + top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 2))->point().z())/3.;
+ double z =
+ (fit->first->vertex(tr.vertex_triple_index(fit->second, 0))->point().z()
+ + fit->first->vertex(tr.vertex_triple_index(fit->second, 1))->point().z()
+ + fit->first->vertex(tr.vertex_triple_index(fit->second, 2))->point().z())/3.;
+ if (top_z < z)
+ top_facet = fit;
+ }
+
+ // - orient the facet with max z towards +Z axis
+ Vertex_handle v0 = top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 0));
+ Vertex_handle v1 = top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 1));
+ Vertex_handle v2 = top_facet->first->vertex(tr.vertex_triple_index(top_facet->second, 2));
+ Vector normal = cross_product(v1->point()-v0->point(), v2->point()-v1->point());
+ const Vector Z(0, 0, 1);
+ bool regular_orientation = (Z * normal >= 0);
+
+ // used to set indices of vertices
+ std::map<Vertex_handle, std::size_t> V;
+ std::size_t inum = 0;
+
+ for(typename std::set<Facet>::const_iterator fit =
+ oriented_set.begin();
+ fit != oriented_set.end();
+ ++fit)
+ {
+ std::size_t indices[3];
+ std::size_t index = 0;
+ for (std::size_t i = 0; i<3; i++)
+ {
+ indices[index++] = get_vertex_index(vertices,
+ fit->first->vertex(tr.vertex_triple_index(fit->second, i)),
+ V,
+ inum);
+ }
+
+ facets.push_back({ {indices[0],
+ regular_orientation ? indices[1] : indices[2],
+ regular_orientation ? indices[2] : indices[1]} });
+ }
+}
+}
+
+namespace mshr
+{
+//-----------------------------------------------------------------------------
+void mshr::SurfaceReconstruction::reconstruct(const std::vector<double>& vertices,
+ const std::vector<std::size_t>& facets,
+ std::vector<std::array<double, 3>>& reconstructed_vertices,
+ std::vector<std::array<std::size_t, 3>>& reconstructed_facets,
+ double expansion)
+{
+ // Poisson options
+ FT sm_angle = 20.0; // Min triangle angle in degrees.
+ FT sm_radius = 10; // Max triangle size w.r.t. point set average spacing.
+ FT sm_distance = 0.10; // Surface Approximation error w.r.t. point set average spacing.
+
+ // Reads the point set file in points[].
+ // Note: read_xyz_points_and_normals() requires an iterator over points
+ // + property maps to access each point's position and normal.
+ // The position property map can be omitted here as we use iterators over Point_3 elements.
+ PointList points;
+ for (std::size_t i = 0; i < facets.size(); i += 3)
+ {
+ const Point a(vertices[facets[i]*3], vertices[facets[i]*3+1], vertices[facets[i]*3+2]);
+ const Point b(vertices[facets[i+1]*3], vertices[facets[i+1]*3+1], vertices[facets[i+1]*3+2]);
+ const Point c(vertices[facets[i+2]*3], vertices[facets[i+2]*3+1], vertices[facets[i+2]*3+2]);
+
+ // compute normal
+ const Vector normal = CGAL::cross_product(b-a, c-a);
+ const Point centroid = CGAL::ORIGIN + ((a-CGAL::ORIGIN)+(b-CGAL::ORIGIN)+(c-CGAL::ORIGIN))/3.0;
+ const Vector normal_normalized = normal/std::sqrt(normal.squared_length());
+ Point_with_normal pm( centroid + normal_normalized*expansion,
+ normal_normalized );
+
+ points.push_back(pm);
+ }
+
+ // Creates implicit function from the read points using the default solver.
+ // Note: this method requires an iterator over points
+ // + property maps to access each point's position and normal.
+ // The position property map can be omitted here as we use iterators over Point_3 elements.
+ log(dolfin::TRACE, "Construct implicit function");
+ Poisson_reconstruction_function function(points.begin(), points.end(),
+ CGAL::make_normal_of_point_with_normal_map(PointList::value_type()) );
+ log(dolfin::TRACE, "Compute Poisson indicator function");
+ // Computes the Poisson indicator function f()
+ // at each vertex of the triangulation.
+ if ( ! function.compute_implicit_function() )
+ {
+ dolfin::dolfin_error("SurfaceReconstruction.cpp",
+ "reconstruct surface",
+ "couldn't compute implicit function");
+ }
+ // Computes average spacing
+ log(dolfin::TRACE, "Compute average spacing");
+ FT average_spacing = CGAL::compute_average_spacing<CGAL::Sequential_tag>(points.begin(), points.end(),
+ 6 /* knn = 1 ring */);
+ // Gets one point inside the implicit surface
+ // and computes implicit function bounding sphere radius.
+ Point inner_point = function.get_inner_point();
+ Sphere bsphere = function.bounding_sphere();
+ FT radius = std::sqrt(bsphere.squared_radius());
+ // Defines the implicit surface: requires defining a
+ // conservative bounding sphere centered at inner point.
+ FT sm_sphere_radius = 5.0 * radius;
+ FT sm_dichotomy_error = sm_distance*average_spacing/1000.0; // Dichotomy error must be << sm_distance
+ Surface_3 surface(function,
+ Sphere(inner_point,sm_sphere_radius*sm_sphere_radius),
+ sm_dichotomy_error/sm_sphere_radius);
+
+ // Defines surface mesh generation criteria
+ CGAL::Surface_mesh_default_criteria_3<STr> criteria(sm_angle, // Min triangle angle (degrees)
+ sm_radius*average_spacing, // Max triangle size
+ sm_distance*average_spacing); // Approximation error
+ // Generates surface mesh with manifold option
+ log(dolfin::TRACE, "Invoke surface mesher");
+ STr tr; // 3D Delaunay triangulation for surface mesh generation
+ C2t3 c2t3(tr); // 2D complex in 3D Delaunay triangulation
+ CGAL::make_surface_mesh(c2t3, // reconstructed mesh
+ surface, // implicit surface
+ criteria, // meshing criteria
+ CGAL::Manifold_with_boundary_tag()); // require manifold mesh
+ if(tr.number_of_vertices() == 0)
+ dolfin::dolfin_error("SurfaceReconstruction.cpp",
+ "reconstruct surface from point set",
+ "Couldn't reconstruct surface");
+
+ export_triangulation(c2t3,
+ reconstructed_vertices,
+ reconstructed_facets);
+}
+ //-----------------------------------------------------------------------------
+void SurfaceReconstruction::remesh(double edge_length,
+ double sharp_edge_tolerance,
+ const std::vector<double>& vertices,
+ const std::vector<std::size_t>& facets,
+ std::vector<std::array<double, 3>>& remeshed_vertices,
+ std::vector<std::array<std::size_t, 3>>& remeshed_facets)
+{
+ typedef CGAL::Surface_mesh<Point> Surface_mesh;
+ typedef Surface_mesh::Vertex_index Vertex_index;
+ typedef Surface_mesh::Face_index Face_index;
+ typedef Surface_mesh::Edge_index Edge_index;
+ typedef Surface_mesh::Halfedge_index Halfedge_index;
+ typedef boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
+ typedef boost::graph_traits<Surface_mesh>::edge_descriptor edge_descriptor;
+
+ namespace Parameters = CGAL::Polygon_mesh_processing::parameters;
+
+ Surface_mesh m;
+ std::vector<Vertex_index> v_index_mapping;
+ v_index_mapping.reserve(vertices.size());
+ for (std::size_t i = 0; i < vertices.size(); i += 3)
+ {
+ v_index_mapping.push_back(m.add_vertex(Point(vertices[i], vertices[i+1], vertices[i+2])));
+ }
+
+ for (std::size_t i = 0; i < facets.size(); i += 3)
+ {
+ m.add_face(v_index_mapping[facets[i]],
+ v_index_mapping[facets[i+1]],
+ v_index_mapping[facets[i+2]]);
+ }
+
+ // Collect edges that should be protected during remeshing
+ const double cos_tolerance = cos(2*DOLFIN_PI*sharp_edge_tolerance/360);
+
+ Surface_mesh::Property_map<edge_descriptor, bool> edge_constrained_map =
+ m.add_property_map<edge_descriptor, bool>("e:is_constrained", false).first;
+
+ std::size_t num_protected = 0;
+ if (sharp_edge_tolerance > 0)
+ {
+ Surface_mesh::Property_map<face_descriptor, Vector> fnormals =
+ m.add_property_map<face_descriptor, Vector>("f:normals", CGAL::NULL_VECTOR).first;
+
+ CGAL::Polygon_mesh_processing::compute_face_normals(m, fnormals);
+
+ for (Edge_index e : m.edges())
+ {
+ Halfedge_index h = e.halfedge();
+ Face_index f1 = m.face(h);
+ Face_index f2 = m.face(m.opposite(h));
+ const Vector& n1 = fnormals[f1];
+ const Vector& n2 = fnormals[f2];
+
+ if (n1*n2 < cos_tolerance)
+ {
+ edge_constrained_map[e] = true;
+ num_protected++;
+ }
+ }
+
+ m.remove_property_map(fnormals);
+
+ dolfin::log(dolfin::TRACE,
+ "Number of protected edges: %u", num_protected);
+ }
+
+ // Remeshing
+ CGAL::Polygon_mesh_processing::isotropic_remeshing(faces(m),
+ edge_length,
+ m,
+ Parameters::edge_is_constrained_map(edge_constrained_map)
+ );
+
+ remeshed_vertices.clear();
+ remeshed_facets.clear();
+
+ std::vector<std::size_t> vertex_index_mapping;
+ vertex_index_mapping.resize(m.num_vertices());
+ int i = 0;
+ for(Vertex_index vd : m.vertices())
+ {
+ const Point& p = m.point(vd);
+ remeshed_vertices.push_back({{CGAL::to_double(p[0]), CGAL::to_double(p[1]), CGAL::to_double(p[2])}});
+ vertex_index_mapping[vd] = i;
+ i++;
+ }
+
+ for (Face_index f : m.faces())
+ {
+ const Halfedge_index h = m.halfedge(f);
+ const Halfedge_index h_next = m.next(h);
+ const Halfedge_index h_next_next = m.next(h_next);
+
+ remeshed_facets.push_back({{vertex_index_mapping[m.source(h)],
+ vertex_index_mapping[m.source(h_next)],
+ vertex_index_mapping[m.source(h_next_next)]}});
+ }
+}
+
+} // end namespace mshr
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <mshr/TetgenMeshGenerator3D.h>
+
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/mesh/CellType.h>
+#include <dolfin/mesh/MeshEditor.h>
+#include <dolfin/mesh/MeshPartitioning.h>
+#include <dolfin/log/log.h>
+
+#include <tetgen.h>
+
+// Bounding sphere computation
+#include <CGAL/Cartesian.h>
+#include <CGAL/Min_sphere_of_spheres_d.h>
+#include <CGAL/Min_sphere_of_spheres_d_traits_3.h>
+
+#include <vector>
+#include <array>
+
+namespace
+{
+//-----------------------------------------------------------------------------
+void build_dolfin_mesh(const tetgenio& tetgenmesh, dolfin::Mesh& dolfinmesh)
+{
+ // Clear mesh
+ // dolfin::Mesh::clear has been removed
+ // dolfinmesh.clear();
+
+ // Create and initialize mesh editor
+ dolfin::MeshEditor mesh_editor;
+ mesh_editor.open(dolfinmesh, dolfin::CellType::Type::tetrahedron, 3, 3);
+ mesh_editor.init_vertices(tetgenmesh.numberofpoints);
+
+ const int offset = tetgenmesh.firstnumber;
+
+ for (int i = 0; i < tetgenmesh.numberofpoints; i++)
+ {
+
+ dolfin::Point p(tetgenmesh.pointlist[i * 3],
+ tetgenmesh.pointlist[i * 3 + 1],
+ tetgenmesh.pointlist[i * 3 + 2]);
+ mesh_editor.add_vertex(i, p);
+ }
+
+
+ mesh_editor.init_cells(tetgenmesh.numberoftetrahedra);
+ dolfin_assert(tetgenmesh.numberofcorners == 4);
+
+ for (int i = 0; i < tetgenmesh.numberoftetrahedra; i++)
+ {
+ mesh_editor.add_cell(i,
+ tetgenmesh.tetrahedronlist[i*4 + 0]-offset,
+ tetgenmesh.tetrahedronlist[i*4 + 1]-offset,
+ tetgenmesh.tetrahedronlist[i*4 + 2]-offset,
+ tetgenmesh.tetrahedronlist[i*4 + 3]-offset);
+ }
+
+ // Close mesh editor
+ mesh_editor.close();
+}
+//-----------------------------------------------------------------------------
+double bounding_sphere_radius(const std::vector<double>& vertices)
+{
+ typedef double FT;
+ typedef CGAL::Cartesian<FT> K;
+ typedef CGAL::Min_sphere_of_spheres_d_traits_3<K, FT> MinSphereTraits;
+ typedef CGAL::Min_sphere_of_spheres_d<MinSphereTraits> Min_sphere;
+ typedef MinSphereTraits::Sphere Sphere;
+
+ std::vector<Sphere> S;
+
+ for (std::size_t i = 0; i < vertices.size(); i += 3)
+ {
+ S.push_back(Sphere(K::Point_3( vertices[i],
+ vertices[i+1],
+ vertices[i+2]), 0.0));
+ }
+
+ Min_sphere ms(S.begin(), S.end());
+ dolfin_assert(ms.is_valid());
+
+ return ms.radius();
+}
+
+} // end anonymous namespace
+//-----------------------------------------------------------------------------
+namespace mshr
+{
+
+TetgenMeshGenerator3D::TetgenMeshGenerator3D()
+{
+ parameters = default_parameters();
+}
+//-----------------------------------------------------------------------------
+TetgenMeshGenerator3D::~TetgenMeshGenerator3D()
+{
+}
+//-----------------------------------------------------------------------------
+std::shared_ptr<dolfin::Mesh>
+TetgenMeshGenerator3D::generate(std::shared_ptr<const CSGCGALDomain3D> domain) const
+{
+ tetgenio in;
+
+ double r;
+ {
+ // Copy the vertices to the tetgen structure
+ dolfin::log(dolfin::TRACE, "Copying vertices");
+ std::unique_ptr<const std::vector<double>> vertices = domain->get_vertices();
+ r = bounding_sphere_radius(*vertices);
+
+ in.numberofpoints = vertices->size()/3;
+ in.pointlist = new REAL[in.numberofpoints * 3];
+
+ for (std::size_t i = 0; i < vertices->size(); i++)
+ {
+ in.pointlist[i] = (*vertices)[i];
+ }
+ }
+
+ // Copy the facets to the tetgen structure
+ {
+ dolfin::log(dolfin::TRACE, "Copying facets");
+ std::unique_ptr<const std::vector<std::size_t>> facets = domain->get_facets();
+
+ in.numberoffacets = facets->size()/3;
+ in.facetlist = new tetgenio::facet[in.numberoffacets];
+ //in.facetmarkerlist = new int[in.numberoffacets];
+
+ for (std::size_t i = 0; i*3 < facets->size(); i++)
+ {
+ tetgenio::facet& f = in.facetlist[i];
+ f.numberofpolygons = 1;
+ f.polygonlist = new tetgenio::polygon[f.numberofpolygons];
+ f.numberofholes = 0;
+ f.holelist = NULL;
+ tetgenio::polygon& p = f.polygonlist[0];
+ p.numberofvertices = 3;
+ p.vertexlist = new int[p.numberofvertices];
+ p.vertexlist[0] = (*facets)[i*3];
+ p.vertexlist[1] = (*facets)[i*3+1];
+ p.vertexlist[2] = (*facets)[i*3+2];
+ }
+ }
+
+ // Mark holes in the domain
+ {
+ dolfin::log(dolfin::TRACE, "Marking holes");
+ std::vector<dolfin::Point> holes;
+ domain->get_points_in_holes(holes);
+
+ in.numberofholes = holes.size();
+ in.holelist = new REAL[in.numberofholes*3];
+ std::size_t i = 0;
+ for (std::vector<dolfin::Point>::const_iterator it = holes.begin();
+ it != holes.end(); it++)
+ {
+ in.holelist[i*3] = it->x();
+ in.holelist[i*3 + 1] = it->y();
+ in.holelist[i*3 + 2] = it->z();
+ }
+
+ i++;
+ }
+
+ // Release domain object, possibly deleting it
+ domain.reset();
+
+ // set tetgen parameters
+ std::stringstream tetgenparams;
+ tetgenparams << std::fixed << std::setprecision(16);
+
+ // tetrahedralize a plc
+ tetgenparams << "p";
+
+ if (!parameters["disable_quality_improvement"])
+ {
+ // set quality constraints
+ const double ratio = parameters["max_radius_edge_ratio"];
+ const double angle = parameters["min_dihedral_angle"];
+ tetgenparams << "q" << ratio << "/" << angle;
+
+ tetgenparams << "a";
+ if (double(parameters["max_tet_volume"]) > 0)
+ {
+ // set maximum cell volume
+ tetgenparams << double(parameters["max_tet_volume"]);
+ }
+ else
+ {
+ const double resolution = parameters["mesh_resolution"];
+
+ // try to compute reasonable parameters
+ //const double r = bounding_sphere_radius(vertices);
+ const double cell_size = r/static_cast<double>(resolution)*2.0;
+ tetgenparams << cell_size;
+ }
+
+ if (parameters["preserve_surface"])
+ {
+ tetgenparams << "Y";
+ }
+ }
+
+ if (dolfin::get_log_level() > dolfin::DBG)
+ {
+ // set verbosity level
+ tetgenparams << "Q";
+ }
+
+ dolfin::log(dolfin::TRACE, "Calling tetgen with parameters: " + tetgenparams.str());
+
+ // Tetgen requires a char[] (as opposed to a const char[])
+ // so we need to copy of from the string
+ const std::string str = tetgenparams.str();
+ std::unique_ptr<char> writable(new char[str.size() + 1]);
+ std::copy(str.begin(), str.end(), writable.get());
+ writable.get()[str.size()] = '\0'; // terminating 0
+
+ tetgenio out;
+ tetrahedralize(writable.get(), &in, &out);
+
+ std::shared_ptr<dolfin::Mesh> mesh(new dolfin::Mesh());
+ build_dolfin_mesh(out, *mesh);
+
+ // Distribute the mesh (if in parallel)
+ dolfin::MeshPartitioning::build_distributed_mesh(*mesh);
+
+ return mesh;
+}
+//-----------------------------------------------------------------------------
+} // end namespace mshr
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef __MSHR_MAKE_MULTICOMPONENT_MESH_3_H
+#define __MSHR_MAKE_MULTICOMPONENT_MESH_3_H
+
+#include <CGAL/make_mesh_3.h>
+#include <CGAL/refine_mesh_3.h>
+
+
+template<class C3T3, class MeshDomain, class MeshCriteria>
+void make_multicomponent_mesh_3_impl(C3T3& c3t3,
+ const MeshDomain& domain,
+ const MeshCriteria& criteria,
+ const CGAL::parameters::internal::Exude_options& exude,
+ const CGAL::parameters::internal::Perturb_options& perturb,
+ const CGAL::parameters::internal::Odt_options& odt,
+ const CGAL::parameters::internal::Lloyd_options& lloyd,
+ const bool with_features,
+ const CGAL::parameters::internal::Mesh_3_options&
+ mesh_options = CGAL::parameters::internal::Mesh_3_options())
+{
+ //std::cout << "Number of vertices initially: " << c3t3.triangulation().number_of_vertices() << std::endl;
+
+ // Initialize c3t3 with points from the special features
+ CGAL::internal::Mesh_3::C3t3_initializer<
+ C3T3,
+ MeshDomain,
+ MeshCriteria,
+ CGAL::internal::Mesh_3::has_Has_features<MeshDomain>::value > () (c3t3,
+ domain,
+ criteria,
+ with_features);
+
+ // std::cout << "Number of vertices after features: " << c3t3.triangulation().number_of_vertices() << std::endl;
+
+ // Inserts points from all connected components to the mesh
+ CGAL::internal::Mesh_3::init_c3t3(c3t3, domain, criteria, 0);
+ // std::cout << "Number of vertices before meshing: " << c3t3.triangulation().number_of_vertices() << std::endl;
+
+ // Build mesher and launch refinement process
+ // Don't reset c3t3 as we just created it
+ refine_mesh_3(c3t3, domain, criteria,
+ exude, perturb, odt, lloyd, CGAL::parameters::no_reset_c3t3(), mesh_options);
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2014-2015 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __MESH_CLEAN_H
+#define __MESH_CLEAN_H
+
+#include <CGAL/basic.h>
+#include <CGAL/Kernel/global_functions.h>
+#include <CGAL/Triangle_3.h>
+
+//-----------------------------------------------------------------------------
+// Get squared edge length
+template<typename Polyhedron>
+inline double
+get_edge_length(typename Polyhedron::Halfedge_const_handle halfedge)
+{
+ return CGAL::to_double((halfedge->vertex()->point() -
+ halfedge->opposite()->vertex()->point()).squared_length());
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline double get_triangle_area(typename Polyhedron::Facet_handle facet)
+{
+ typedef typename Polyhedron::Traits::Triangle_3 Triangle;
+ typename Polyhedron::Halfedge_const_handle h = facet->halfedge();
+ Triangle t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ return CGAL::to_double(t.squared_area());
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline typename Polyhedron::Halfedge_handle
+ get_longest_edge(typename Polyhedron::Facet_handle facet)
+{
+ typename Polyhedron::Halfedge_handle current = facet->halfedge();
+ typename Polyhedron::Halfedge_handle longest = current;
+ double length = get_edge_length<Polyhedron>(current);
+
+ current = current->next();
+ if (get_edge_length<Polyhedron>(current) > length)
+ {
+ length = get_edge_length<Polyhedron>(current);
+ longest = current;
+ }
+
+ current = current->next();
+ if (get_edge_length<Polyhedron>(current) > length)
+ {
+ length = get_edge_length<Polyhedron>(current);
+ longest = current;
+ }
+
+ return longest;
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline typename Polyhedron::Halfedge_const_handle
+ get_longest_const_edge(typename Polyhedron::Facet_const_handle facet)
+{
+ typename Polyhedron::Halfedge_const_handle current = facet->halfedge();
+ typename Polyhedron::Halfedge_const_handle longest = current;
+ double length = get_edge_length<Polyhedron>(current);
+
+ current = current->next();
+ if (get_edge_length<Polyhedron>(current) > length)
+ {
+ length = get_edge_length<Polyhedron>(current);
+ longest = current;
+ }
+
+ current = current->next();
+ if (get_edge_length<Polyhedron>(current) > length)
+ {
+ length = get_edge_length<Polyhedron>(current);
+ longest = current;
+ }
+
+ return longest;
+}
+//-----------------------------------------------------------------------------
+// Compute the distance from the longest edge to the opposite vertex
+template<typename Polyhedron>
+inline double triangle_projection(typename Polyhedron::Facet_const_handle f)
+{
+ typename Polyhedron::Halfedge_const_handle longest = get_longest_const_edge<Polyhedron>(f);
+ typename Polyhedron::Traits::Line_3 l(longest->vertex()->point(), longest->opposite()->vertex()->point());
+ return CGAL::to_double( (l.projection(longest->next()->vertex()->point()) - longest->next()->vertex()->point()).squared_length());
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline double
+get_min_edge_length(typename Polyhedron::Facet_const_handle facet)
+{
+ typename Polyhedron::Facet::Halfedge_around_facet_const_circulator half_edge
+ = facet->facet_begin();
+ double min_length = CGAL::to_double((half_edge->vertex()->point()
+ - half_edge->opposite()->vertex()->point()).squared_length());
+
+ half_edge++;
+ min_length = std::min(min_length, get_edge_length<Polyhedron>(half_edge));
+
+ half_edge++;
+ min_length = std::min(min_length, get_edge_length<Polyhedron>(half_edge));
+
+ return min_length;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline double
+get_max_edge_length(typename Polyhedron::Facet_const_handle facet)
+{
+ typename Polyhedron::Facet::Halfedge_around_facet_const_circulator h = facet->facet_begin();
+ double max_length = get_edge_length<Polyhedron>(h);
+
+ h++;
+ max_length = std::max(max_length, get_edge_length<Polyhedron>(h));
+
+ h++;
+ max_length = std::max(max_length, get_edge_length<Polyhedron>(h));
+
+ return max_length;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+std::size_t get_vertex_id(const Polyhedron& p, typename Polyhedron::Vertex_const_handle v)
+{
+ return std::distance(p.vertices_begin(), v);
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+void print_vertex(const Polyhedron& p, typename Polyhedron::Halfedge_const_handle h)
+{
+ typename Polyhedron::Halfedge_const_handle current = h;
+ do
+ {
+ dolfin_assert(current->vertex() == h->vertex());
+ std::cout << get_vertex_id(p, current->opposite()->vertex()) << std::endl;
+
+ current = current->opposite()->prev();
+ } while(current != h);
+}
+
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+void print_edge(const Polyhedron& p, typename Polyhedron::Halfedge_const_handle h)
+{
+ std::cout << "(" << get_vertex_id(p, h->opposite()->vertex()) << ": " << h->opposite()->vertex()->point() << ", "
+ << get_vertex_id(p, h->vertex()) << ": " << h->vertex()->point() << ")" << std::endl;
+}
+//-----------------------------------------------------------------------------
+// Print some info about a facet
+// Meant for debugging
+template<typename Polyhedron>
+void print_facet(const Polyhedron& p, typename Polyhedron::Halfedge_const_handle h, bool verbose=true)
+{
+ std::cout << "Vertices: " << std::endl;
+ typename Polyhedron::Halfedge_const_handle current = h;
+ do
+ {
+ std::cout << " " << get_vertex_id<Polyhedron>(p, current->vertex()) << ": (" << current->vertex()->point() << ") " << std::endl;
+ current = current ->next();
+ } while (current != h);
+
+ if (verbose)
+ {
+ std::cout << "Edge lengths (squared):" << std::endl;
+ current = h;
+ do
+ {
+ std::cout << " " << (current->vertex()->point() - current->next()->vertex()->point()).squared_length() << std::endl;
+ current = current->next();
+ } while (current != h);
+
+ typename Polyhedron::Traits::Triangle_3 t(h->vertex()->point(),
+ h->next()->vertex()->point(),
+ h->next()->next()->vertex()->point());
+ std::cout << "Area (if triangle): " << t.squared_area() << std::endl;
+
+ typename Polyhedron::Halfedge_const_handle longest = get_longest_const_edge<Polyhedron>(h->facet());
+ typename Polyhedron::Traits::Line_3 l(longest->vertex()->point(), longest->opposite()->vertex()->point());
+ std::cout << "Colinearity (if triangle): " << triangle_projection<Polyhedron>(h->facet()) << std::endl;
+ }
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline bool facet_is_degenerate(typename Polyhedron::Facet_const_handle facet,
+ typename Polyhedron::Traits::FT tol_sq)
+{
+ dolfin_assert(facet->is_triangle());
+
+ return get_min_edge_length<Polyhedron>(facet) < tol_sq
+ || triangle_projection<Polyhedron>(facet) < tol_sq;
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline double shortest_edge(Polyhedron& p)
+{
+ double shortest = std::numeric_limits<double>::max();
+ for (typename Polyhedron::Halfedge_iterator halfedge = p.halfedges_begin();
+ halfedge != p.halfedges_end(); halfedge++)
+ {
+ const double length = get_edge_length<Polyhedron>(halfedge);
+ shortest = std::min(shortest, length);
+ }
+
+ return shortest;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+int number_of_degenerate_facets(const Polyhedron& p, const double tolerance)
+{
+ int count = 0;
+ for (typename Polyhedron::Facet_const_iterator facet = p.facets_begin();
+ facet != p.facets_end(); facet++)
+ {
+ dolfin_assert(facet->is_triangle());
+ if ( facet_is_degenerate<Polyhedron>(facet, tolerance) )
+ count++;
+ }
+ return count;
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline bool has_slivers(const Polyhedron& p)
+{
+ for (typename Polyhedron::Vertex_const_iterator vit = p.vertices_begin(); vit != p.vertices_end(); vit++)
+ {
+ if (vit->vertex_degree() < 3)
+ {
+ // Check that this is not on a border (we can have borders eg. when
+ // reading files that must be repaired
+ const typename Polyhedron::Halfedge_around_vertex_const_circulator first = vit->vertex_begin();
+ typename Polyhedron::Halfedge_around_vertex_const_circulator current = first;
+
+ bool is_border = false;
+
+ do
+ {
+ if ( !current->is_border() )
+ is_border = true;
+
+ current++;
+ } while (current != first);
+
+ if (!is_border)
+ return true;
+ }
+ }
+
+ /* for (typename Polyhedron::Halfedge_const_iterator it = p.halfedges_begin(); */
+ /* it != p.halfedges_end(); it++) */
+ /* { */
+ /* if (it->next()->vertex() == it->opposite()->next()->vertex()) */
+ /* { */
+ /* return true; */
+ /* } */
+ /* } */
+ return false;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline bool has_degree3_neighbors(const Polyhedron& p)
+{
+ for (typename Polyhedron::Halfedge_const_iterator it = p.halfedges_begin();
+ it != p.halfedges_end(); it++)
+ {
+ if (it->vertex()->vertex_degree() < 4 &&
+ it->opposite()->vertex()->vertex_degree() < 4)
+ return true;
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline std::size_t min_vertex_degree(const Polyhedron& p)
+{
+ typename Polyhedron::Vertex_const_iterator vit = p.vertices_begin();
+ std::size_t min_degree = vit->vertex_degree();
+ vit++;
+
+ for (; vit != p.vertices_end(); vit++)
+ min_degree = std::min(min_degree, vit->vertex_degree());
+
+ return min_degree;
+}
+//-----------------------------------------------------------------------------
+#define ASSERT_GOOD_STATE(p) do \
+{ \
+ dolfin_assert(p.is_valid()); \
+ dolfin_assert(p.is_pure_triangle()); \
+ dolfin_assert(!has_slivers(p)); \
+ dolfin_assert(p.is_closed()); \
+ dolfin_assert(min_vertex_degree(p) > 2); \
+ dolfin_assert(p.size_of_vertices() < 5 || !has_degree3_neighbors(p)); \
+} while(false);
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline void remove_degree3_center_vertex(Polyhedron& p,
+ typename Polyhedron::Halfedge_handle h)
+{
+ // Remove center vertex, but assure the degree of the vertex is 3 and that at least
+ // one of the sides of the incident triangles is short
+ dolfin_assert(h->vertex()->vertex_degree() == 3);
+
+ p.erase_center_vertex(h);
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+inline bool remove_degree3_with_short_edges(Polyhedron& p,
+ typename Polyhedron::Traits::FT tol_sq)
+{
+ bool removed = false;
+
+ typename Polyhedron::Vertex_iterator vit = p.vertices_begin();
+ typename Polyhedron::Vertex_iterator vertex_end = p.vertices_end();
+ while (vit != vertex_end)
+ {
+ // iterator is invalid if we remove the vertex, so copy and advance now
+ typename Polyhedron::Vertex_handle v = vit;
+ vit++;
+
+ if (v->vertex_degree() < 3)
+ log(dolfin::DBG, "Vertex with degree less than 3 detected");
+
+ if (v->is_trivalent())
+ {
+ typename Polyhedron::Halfedge_handle h = v->halfedge();
+ if (get_edge_length<Polyhedron>(h) < tol_sq ||
+ get_edge_length<Polyhedron>(h->next()) < tol_sq ||
+ get_edge_length<Polyhedron>(h->prev()) < tol_sq)
+ {
+ p.erase_center_vertex(h);
+ removed = true;
+ }
+ }
+ }
+ return removed;
+}
+//-----------------------------------------------------------------------------
+template <typename Polyhedron>
+inline void collapse_edge(Polyhedron& p,
+ typename Polyhedron::Halfedge_handle edge)
+{
+ ASSERT_GOOD_STATE(p);
+
+ if (edge->vertex()->is_trivalent())
+ {
+ p.erase_center_vertex(edge);
+ ASSERT_GOOD_STATE(p);
+ }
+ else if (edge->opposite()->vertex()->is_trivalent())
+ {
+ p.erase_center_vertex(edge->opposite());
+ ASSERT_GOOD_STATE(p);
+ }
+ else
+ {
+ // Join small triangles with neighbor facets
+
+ // Make sure we don't introduce slivers
+ // (ie. vertices of degree 2)
+ while (edge->next()->vertex()->is_trivalent())
+ {
+ remove_degree3_center_vertex(p, edge->next());
+ ASSERT_GOOD_STATE(p);
+ }
+
+ while (edge->opposite()->next()->vertex()->is_trivalent())
+ {
+ remove_degree3_center_vertex(p, edge->opposite()->next());
+ ASSERT_GOOD_STATE(p);
+ }
+
+ if (edge->vertex()->is_trivalent())
+ {
+ p.erase_center_vertex(edge);
+ }
+ else if (edge->opposite()->vertex()->is_trivalent())
+ {
+ p.erase_center_vertex(edge->opposite());
+ }
+
+ ASSERT_GOOD_STATE(p);
+ dolfin_assert(edge->vertex()->vertex_degree() > 3);
+
+ edge = p.join_facet(edge->next());
+ dolfin_assert(p.is_valid());
+ dolfin_assert(!has_slivers(p));
+
+ p.join_facet(edge->opposite()->prev());
+ dolfin_assert(p.is_valid());
+
+ dolfin_assert(edge->vertex() != edge->opposite()->vertex());
+
+ // We can possibly have a sliver now
+
+ // The joined facets are now quads
+ // Join the two close vertices
+ p.join_vertex(edge);
+ ASSERT_GOOD_STATE(p);
+ }
+}
+//-----------------------------------------------------------------------------
+// FIXME: Return the number of edges collapsed
+template <typename Polyhedron>
+bool collapse_short_edges(Polyhedron& p,
+ typename Polyhedron::Traits::FT tol_sq)
+{
+ // Degree 3 vertices with short incident edges causes problems when collapsing
+ // short edges. The very ad hoc solution that has shown to work is to remove
+ // these center vertices (and by that the short edges) before collapsing short
+ // edges.
+
+ bool edges_removed = remove_degree3_with_short_edges(p, tol_sq);
+
+ bool removed;
+
+ do
+ {
+ removed = false;
+
+ for (typename Polyhedron::Halfedge_iterator halfedge = p.halfedges_begin();
+ halfedge != p.halfedges_end(); halfedge++)
+ {
+ if (get_edge_length<Polyhedron>(halfedge) < tol_sq)
+ {
+ collapse_edge<Polyhedron>(p, halfedge);
+ ASSERT_GOOD_STATE(p);
+ remove_degree3_with_short_edges(p, tol_sq);
+ removed = true;
+ edges_removed = true;
+ break;
+ }
+ }
+ } while (removed);
+
+ return edges_removed;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+bool flip_edges(Polyhedron& p,
+ typename Polyhedron::Traits::FT tol_sq)
+{
+ typedef typename Polyhedron::Traits::Line_3 Line_3;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+
+ bool edge_flipped = false;
+ bool done;
+
+ do
+ {
+ done = true;
+
+ for (typename Polyhedron::Facet_iterator facet = p.facets_begin();
+ facet != p.facets_end(); facet++)
+ {
+ dolfin_assert(facet->is_triangle());
+ if (triangle_projection<Polyhedron>(facet) < tol_sq)
+ {
+ dolfin_assert(p.is_pure_triangle());
+ dolfin_assert(p.is_closed());
+
+ typename Polyhedron::Halfedge_handle longest
+ = get_longest_edge<Polyhedron>(facet);
+
+ if (longest->vertex()->is_trivalent())
+ remove_degree3_center_vertex(p, longest);
+ else if (longest->opposite()->vertex()->is_trivalent())
+ remove_degree3_center_vertex(p, longest->opposite());
+ else
+ {
+
+ Line_3 l(longest->vertex()->point(),
+ longest->opposite()->vertex()->point());
+ Point_3 newpoint = l.projection(longest->next()->vertex()->point());
+ Halfedge_handle flipped = p.flip_edge(longest);
+ flipped->vertex()->point() = newpoint;
+
+ ASSERT_GOOD_STATE(p);
+
+ // TODO: Check length of newly created edge and
+ // collapse if necessary.
+ collapse_short_edges(p, tol_sq);
+ done = false;
+ edge_flipped = true;
+ }
+ }
+ }
+ } while (!done);
+
+ return edge_flipped;
+}
+//-----------------------------------------------------------------------------
+template<typename Polyhedron>
+bool has_degenerate_facets(const Polyhedron& p,
+ typename Polyhedron::Traits::FT tol_sq)
+{
+ for (typename Polyhedron::Facet_const_iterator facet = p.facets_begin();
+ facet != p.facets_end(); facet++)
+ {
+ dolfin_assert(facet->is_triangle());
+ if (facet_is_degenerate<Polyhedron>(facet, tol_sq))
+ return true;
+ }
+ return false;
+}
+//-----------------------------------------------------------------------------
+// Remove degenerate facets of a triangular polyhedron by
+// 1) Collapse edges with squared length less than tolerance
+// 2) Remove (almost) colinear facets by flipping the longest edge of the
+// colinear facet. Colinearity defined as the (squared) distance from a
+// vertex to the opposite edge being less than tolerance
+template<typename Polyhedron>
+bool remove_degenerate(Polyhedron &p, double tolerance)
+{
+ log(dolfin::TRACE, "Cleaning degenerate facets");
+ dolfin_assert(p.is_pure_triangle());
+
+ // Compute squared tolerance
+ typename Polyhedron::Traits::FT tol_sq(tolerance);
+ tol_sq *= tol_sq;
+
+ const bool edges_removed = remove_degree3_with_short_edges(p, tol_sq);
+ if (edges_removed)
+ log(dolfin::TRACE, "Remove degree 3 vertices");
+
+ ASSERT_GOOD_STATE(p);
+
+
+ log(dolfin::TRACE, " Collapsing short edges");
+ const bool collapsed = collapse_short_edges(p, tol_sq);
+ ASSERT_GOOD_STATE(p);
+
+ // log(dolfin::TRACE, "Shortest edge: %f", shortest_edge());
+ log(dolfin::TRACE, " Removing colinear facets by edge flipping");
+ const bool flipped = flip_edges(p, tol_sq);
+ ASSERT_GOOD_STATE(p);
+
+ dolfin_assert(!has_degenerate_facets(p, tol_sq));
+
+ return collapsed || flipped;
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2016 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __SMOOTHING_H
+#define __SMOOTHING_H
+
+namespace mshr
+{
+
+class LaplacianSmoothing
+{
+ public:
+ template<typename Polyhedron>
+ static void smooth(Polyhedron& p, double c=1.0)
+ {
+ typedef typename Polyhedron::Vertex_handle Vertex_handle;
+ typedef typename Polyhedron::Traits::Point_3 Point_3;
+ typedef typename Polyhedron::Traits::Vector_3 Vector_3;
+ typedef typename Polyhedron::Halfedge_around_vertex_const_circulator HV_const_circulator;
+
+ assert(p.is_valid());
+
+ std::vector<std::pair<Vertex_handle, Point_3>> smoothed;
+
+ for (typename Polyhedron::Vertex_iterator vit = p.vertices_begin();
+ vit != p.vertices_end();
+ vit++)
+ {
+ const Point_3 current = vit->point();
+
+ Vector_3 delta;
+
+ const HV_const_circulator h_start = vit->vertex_begin();
+ HV_const_circulator h_current = h_start;
+ do
+ {
+ delta = delta + (h_current->opposite()->vertex()->point()-current);
+ h_current++;
+ } while (h_current != h_start);
+
+ Point_3 p = current + c*delta/vit->vertex_degree();
+
+ // Evaluate exact value to reduce memory usage
+ p.exact();
+
+ smoothed.push_back(std::make_pair(Vertex_handle(vit), p));
+ }
+
+ // Apply the smoothing
+ for (const std::pair<Vertex_handle, Point_3>& s : smoothed)
+ {
+ s.first->point() = s.second;
+ }
+ }
+};
+
+}
+#endif
--- /dev/null
+// Copyright (C) 2014 Benjamin Kehlet
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef __TRIANGULATION_REFINEMENT_H
+#define __TRIANGULATION_REFINEMENT_H
+
+#include <dolfin/mesh/MeshEditor.h>
+
+#ifndef DEBUG_TRIANGULATION_REFINEMENT
+ #define DEBUG_TRIANGULATION_REFINEMENT 0
+#endif
+
+namespace
+{
+ // Simple convenience class for converting from barycentric coordinates wrt to
+ // a reference triangle.
+ class RefTriangle
+ {
+ public:
+ RefTriangle(dolfin::Point v1, dolfin::Point v2, dolfin::Point v3)
+ : v1(v1), v2(v2), v3(v3) {}
+
+ dolfin::Point barycentric2Point(double l1, double l2, double l3)
+ {
+ const double x = l1*v1.x() + l2*v2.x() + l3*v3.x();
+ const double y = l1*v1.y() + l2*v2.y() + l3*v3.y();
+ const double z = l1*v1.z() + l2*v2.z() + l3*v3.z();
+
+ return dolfin::Point(x, y, z);
+ }
+
+ private:
+ dolfin::Point v1, v2 , v3;
+ };
+ //-----------------------------------------------------------------------------
+ inline dolfin::Point get_edge_point(dolfin::Point a, dolfin::Point b, double f)
+ {
+ // print "Get point {} between {} and {}".format(f, a, b)
+ const dolfin::Point e = b-a;
+ return a + e*f;
+ }
+ //-----------------------------------------------------------------------------
+ inline std::size_t get_edge_vertex(const std::map<std::array<std::size_t,3 >,
+ std::size_t>& edge_vertices,
+ std::size_t a,
+ std::size_t b,
+ std::size_t i,
+ std::size_t N)
+ {
+ const std::array<std::size_t, 3> key{{std::min(a, b), std::max(a, b), a < b ? i : N-i}};
+ std::size_t v = edge_vertices.at(key);
+ // print "getting edge vertex ({}, {}, {}) = {}".format(a, b, i, v)
+ return v;
+ }
+ //-----------------------------------------------------------------------------
+ inline void add_cell(std::vector<std::array<std::size_t, 3> >& triangles, std::array<std::size_t, 3> v)
+ {
+ #if DEBUG_TRIANGULATION_REFINEMENT
+ static std::set<std::pair<std::size_t, std::size_t> > halfedges;
+
+ auto ins = halfedges.insert(std::make_pair(v[0], v[1]));
+ dolfin_assert(ins.second);
+ ins = halfedges.insert(std::make_pair(v[1], v[2]));
+ dolfin_assert(ins.second);
+ ins = halfedges.insert(std::make_pair(v[2], v[0]));
+ dolfin_assert(ins.second);
+ #endif
+
+ triangles.push_back(v);
+ }
+}
+//-----------------------------------------------------------------------------
+// TODO: Use outputiterator to return triangulation instead of vectors to avoid
+// copying
+void refine_triangulation(const std::vector<dolfin::Point> initial_vertices,
+ const std::vector<std::array<std::size_t, 3> > initial_triangulation,
+ std::size_t N,
+ std::vector<dolfin::Point>& vertices,
+ std::vector<std::array<std::size_t, 3> >& triangles)
+{
+ const std::size_t num_vertices = initial_vertices.size() +
+ (N-1)*initial_triangulation.size() + (N+1)*N/2.0;
+ const std::size_t num_triangles = N*N*initial_triangulation.size();
+
+ vertices.clear();
+ triangles.clear();
+ vertices.reserve(num_vertices);
+ triangles.reserve(num_triangles);
+
+ // Add the corner vertices
+ for (const dolfin::Point& p : initial_vertices)
+ {
+ vertices.push_back(p);
+ }
+
+ std::map<std::array<std::size_t, 3>, std::size_t> edge_vertices;
+
+ // Add the "inner" vertices along the edges
+ for (std::size_t i=1; i < N; i++)
+ {
+ for (const std::array<std::size_t, 3>& t : initial_triangulation)
+ {
+ for (std::size_t j = 0; j < 3; j++)
+ {
+ if (t[j] < t[(j+1)%3])
+ {
+ dolfin::Point v = get_edge_point(initial_vertices[t[j]], initial_vertices[t[(j+1)%3]], float(i)/N);
+
+ // NOTE: Some older compilers (eg. gcc on Ubuntu Precise) require the std::array type to be given
+ // explicitly.
+ edge_vertices[std::array<std::size_t,3>{{t[j], t[(j+1)%3], i}}] = vertices.size();
+ vertices.push_back(v/v.norm());
+ }
+ }
+ }
+ }
+
+
+ /***************************** Add the triangles *********************************/
+ std::size_t cell_count = 0;
+ for (const std::array<std::size_t, 3>& triangle : initial_triangulation)
+ {
+ //std::cout << "Processing triangle (" << triangle[0] << ", " << triangle[1] << ", " << triangle[2] << ")" << std::endl;
+ RefTriangle ref_triangle(initial_vertices[triangle[0]], initial_vertices[triangle[1]], initial_vertices[triangle[2]]);
+ const std::size_t vertex_start = vertices.size();
+
+ for (std::size_t i = 1; i < N; i++)
+ {
+ const double l1 = static_cast<double>(i)/N;
+ for (std::size_t j=1; j < N-i; j++)
+ {
+ // Don't edge along initial vertices
+ if (i+j == N)
+ continue;
+
+ const double l2 = static_cast<double>(j)/N;
+ const double l3 = 1.0 - l1 - l2;
+
+ dolfin::Point p = ref_triangle.barycentric2Point(l1, l2, l3);
+ p /= p.norm();
+ vertices.push_back(p);
+ }
+ }
+
+ // std::cout << " Adding corner facets" << std::endl;
+ add_cell(triangles, std::array<std::size_t, 3>{{ triangle[0],
+ get_edge_vertex(edge_vertices,
+ triangle[0],
+ triangle[1],
+ 1, N),
+ get_edge_vertex(edge_vertices,
+ triangle[0], triangle[2],
+ 1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{triangle[1],
+ get_edge_vertex(edge_vertices,
+ triangle[1],
+ triangle[2],
+ 1, N),
+ get_edge_vertex(edge_vertices,
+ triangle[1],
+ triangle[0],
+ 1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{triangle[2],
+ get_edge_vertex(edge_vertices,
+ triangle[2],
+ triangle[0],
+ 1, N),
+ get_edge_vertex(edge_vertices,
+ triangle[2],
+ triangle[1],
+ 1, N)}});
+ cell_count += 1;
+
+
+ if (N == 2)
+ {
+ // std::cout << " Refinement level 2: Adding interior cell" << std::endl;
+ add_cell(triangles, std::array<std::size_t, 3>{{get_edge_vertex(edge_vertices, triangle[0], triangle[1], 1, N),
+ get_edge_vertex(edge_vertices, triangle[1], triangle[2], 1, N),
+ get_edge_vertex(edge_vertices, triangle[2], triangle[0], 1, N)}});
+ cell_count += 1;
+ }
+ else
+ {
+ // std::cout << " Add facets incident to original edges" << std::endl;
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start,
+ get_edge_vertex(edge_vertices, triangle[2], triangle[1], 1, N),
+ get_edge_vertex(edge_vertices, triangle[2], triangle[0], 1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+N-3,
+ get_edge_vertex(edge_vertices, triangle[1], triangle[0], 1, N),
+ get_edge_vertex(edge_vertices, triangle[1], triangle[2], 1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{ vertices.size() - 1,
+ get_edge_vertex(edge_vertices,triangle[0], triangle[2], 1, N),
+ get_edge_vertex(edge_vertices,triangle[0], triangle[1], 1, N)}});
+ cell_count += 1;
+
+
+ // std::cout << " Add the facets along the original edges" << std::endl;
+
+ // along edge(triangle[1] <--> triangle[2])
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start,
+ get_edge_vertex(edge_vertices,triangle[2], triangle[1], 2, N),
+ get_edge_vertex(edge_vertices,triangle[2], triangle[1], 1, N)}});
+ cell_count += 1;
+
+ for (std::size_t i = 3; i < N; i++)
+ {
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+i-3,
+ vertex_start+i-2,
+ get_edge_vertex(edge_vertices,triangle[2], triangle[1], i-1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+i-2,
+ get_edge_vertex(edge_vertices,triangle[2], triangle[1], i, N),
+ get_edge_vertex(edge_vertices,triangle[2], triangle[1], i-1, N)}});
+ cell_count += 1;
+ }
+
+ // along edge(triangle[2], triangle[0])
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start,
+ get_edge_vertex(edge_vertices,triangle[2], triangle[0], 1, N),
+ get_edge_vertex(edge_vertices,triangle[2], triangle[0], 2, N)}});
+ cell_count += 1;
+
+ std::size_t current_inner_vertex = 0;
+ for (std::size_t i = 3; i < N; i++)
+ {
+ const std::size_t step = N-2-i+3;
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+current_inner_vertex+step,
+ vertex_start+current_inner_vertex,
+ get_edge_vertex(edge_vertices,triangle[2], triangle[0], i-1, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{get_edge_vertex(edge_vertices,triangle[2], triangle[0], i-1, N),
+ get_edge_vertex(edge_vertices,triangle[2], triangle[0], i, N),
+ vertex_start+current_inner_vertex+step}});
+ cell_count += 1;
+
+ current_inner_vertex += step;
+ }
+
+ // along edge(triangle[1], triangle[0])
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+N-3,
+ get_edge_vertex(edge_vertices,triangle[1], triangle[0], 2, N),
+ get_edge_vertex(edge_vertices,triangle[1], triangle[0], 1, N)}});
+ cell_count += 1;
+
+ current_inner_vertex = N-3;
+ for (std::size_t i = 3; i < N; i++)
+ {
+ const std::size_t step = N-3-i+3;
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+current_inner_vertex,
+ vertex_start+current_inner_vertex+step,
+ get_edge_vertex(edge_vertices,triangle[1], triangle[0], i-3+2, N)}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+current_inner_vertex+step,
+ get_edge_vertex(edge_vertices,triangle[1], triangle[0], i-3+3, N),
+ get_edge_vertex(edge_vertices,triangle[1], triangle[0], i-3+2, N)}});
+ cell_count += 1;
+
+ current_inner_vertex += step;
+ }
+ }
+
+ // std::cout << " Add the inner facets that don't touch initial edges" << std::endl;
+
+ std::size_t row_offset = 0;
+ for (std::size_t i = 1; i < N-2; i++)
+ {
+ const std::size_t row_length = N-1-i;
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+row_offset,
+ vertex_start+row_offset+row_length,
+ vertex_start+row_offset+1}});
+ cell_count += 1;
+
+ for (std::size_t j = 0; j < N-3-i; j++)
+ {
+ add_cell(triangles,
+ std::array<std::size_t, 3>{{vertex_start+row_offset+row_length+j,
+ vertex_start+row_offset+row_length+j+1,
+ vertex_start+row_offset+j+1}});
+ cell_count += 1;
+
+ add_cell(triangles, std::array<std::size_t, 3>{{vertex_start+row_offset+row_length+j+1,
+ vertex_start+row_offset+j+2,
+ vertex_start+row_offset+j+1}});
+ cell_count += 1;
+ }
+ row_offset += row_length;
+ }
+ }
+}
+
+#endif
--- /dev/null
+# Copyright (C) 2014 Benjamin Kehlet
+#
+# This file is part of mshr.
+#
+# mshr 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 3 of the License, or
+# (at your option) any later version.
+#
+# mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+
+# This file defines regression tests.
+
+# Make sure python finds the mshr module.
+# NOTE: Not sure this is the best way. Is saves the environment at "cmake
+# time" and applies it when running the test. Changing PYTHONPATH
+# later will not have effect. Not sure how to add tp PYTHONPATH at
+# runtime.
+set(PYTHON_ENVIR "$ENV{PYTHONPATH};DOLFIN_NOPLOT=True")
+
+
+############# A python program that just imports dolfin and mshr ##############################
+add_test("Python-DummyImport" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/dummy.py")
+set_property(TEST "Python-DummyImport" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# A union which is particularly prone to roundoff errors ##########################
+add_test("Python-DegenerateRemoval" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/degenerate_removal.py")
+set_property(TEST "Python-DegenerateRemoval" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Test the csg operators ##########################################################
+add_test("Python-CSGOperators" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-csg.py")
+set_property(TEST "Python-CSGOperators" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Test the fuzzy point map ########################################################
+# This test is disabled since the Surface3D class is about to be removed.
+#add_test("Python-FuzzyPointMap" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-fuzzypointmap.py")
+#set_property(TEST "Python-FuzzyPointMap" PROPERTY
+# ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+#)
+
+############# Test the mesh generation ########################################################
+add_test("Python-MeshGeneration" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-mesh-generation.py")
+set_property(TEST "Python-MeshGeneration" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Test the predefined meshes ##########################################################
+add_test("Python-Meshes" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-meshes.py")
+set_property(TEST "Python-Meshes" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+############# Test the ASCFileReader ##########################################################
+add_test("Python-ASCFileReader" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-ASCFileReader.py")
+set_property(TEST "Python-ASCFileReader" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Test the ASCFileReader ##########################################################
+add_test("Python-NumSegments2D" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-num-segments-2d.py")
+set_property(TEST "Python-NumSegments2D" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Test the CSG predicates (2D) ##########################################################
+add_test("Python-CSGPredicates2D" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-csg-predicates.py")
+set_property(TEST "Python-CSGPredicates2D" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+ )
+
+############# Test CSG primitves (2D) ##########################################################
+add_test("Python-CSGPrimitives2D" "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/test/test-csg-primitives-2d.py")
+set_property(TEST "Python-CSGPredicates2D" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+)
+
+############# Try meshing some surface files found in the cgal source ########################
+# TODO: Print mesh quality measure from program and check the value
+set(CGAL_DATA_DIR "${CMAKE_SOURCE_DIR}/3rdparty/CGAL/demo/Polyhedron/data")
+
+# Files that should pass
+# Note elephant.off is valid a should passes but takes a very long (so test slave may time out)
+set(TESTFILES_VALID "anchor.off;couplingdown.off;cross.off;cube.off;dragknob.off;ellipsoid.off;handle.off;icosahedron.off;joint.off;knot1.off;knot2.off;pinion.off;pipe.off;rotor.off;sphere.off;spool.off;star.off;translated-cube.off;tripod.off")
+set(TESTFILES_SELFINTERSECTING "bones.off;cow.off;man.off;oblong.ogg")
+set(TESTFILES_NOTCLOSED "cube-ouvert.off;mushroom.off")
+set(TESTFILES_NOTTRIANGULAR "pyramid.off")
+
+foreach(CURRENT_DATA_FILE IN LISTS TESTFILES_VALID)
+ message(STATUS "Adding test: ${CURRENT_DATA_FILE}")
+ add_test(FilePass-cgal-${CURRENT_DATA_FILE} ../mshrable -s -b cgal --check-mesh ${CGAL_DATA_DIR}/${CURRENT_DATA_FILE})
+ add_test(FilePass-tetgen-${CURRENT_DATA_FILE} ../mshrable -s -b tetgen --check-mesh ${CGAL_DATA_DIR}/${CURRENT_DATA_FILE})
+endforeach(CURRENT_DATA_FILE)
+
+
+############## CPP tests
+add_executable(cppTestCSGGeometries test-csggeometries.cpp)
+target_link_libraries(cppTestCSGGeometries mshr)
+add_test(CPP-CSGGeometries cppTestCSGGeometries)
+
+
+############ Run the demos as regression test ################################################
+file(GLOB PYTHONDEMOS "${CMAKE_SOURCE_DIR}/demo/python/*.py")
+foreach(CURRENT_DEMO IN LISTS PYTHONDEMOS)
+ add_test("PythonDemo-${CURRENT_DEMO}" "${PYTHON_EXECUTABLE}" "${CURRENT_DEMO}")
+ set_property(TEST "PythonDemo-${CURRENT_DEMO}" PROPERTY
+ ENVIRONMENT "PYTHONPATH=${PYTHON_ENVIR}"
+ )
+
+endforeach()
--- /dev/null
+from mshr import *
+
+# This union of spheres challenges the removal of degenerate facets
+# since the intersection polyline of the two spheres matches lines in
+# the triangulation of the spheres. Because of that roundoff errors
+# introduces a lot of very small triangles when the union is carried
+# out.
+
+a = Sphere(dolfin.Point(0,0,0), .5)
+b = Sphere(dolfin.Point(.5,0,0), .5)
+
+domain = CSGCGALDomain3D(a+b)
+domain.ensure_meshing_preconditions()
+
--- /dev/null
+import dolfin
+import mshr
+
+
+
--- /dev/null
+from dolfin import *
+from mshr import *
+
+import os
+import tempfile
+
+def save_surface(filename) :
+ geometry = Sphere(Point(0.0,0.0,0.0),10.)
+ domain = CSGCGALDomain3D(geometry)
+ domain.save(filename)
+
+def load_surface(filename) :
+ surf = Surface3D(filename)
+ domain = CSGCGALDomain3D(surf)
+ mesh = generate_mesh(domain, 10)
+
+fd, temp_path = tempfile.mkstemp(suffix='.asc')
+
+save_surface(temp_path)
+load_surface(temp_path)
+
+os.close(fd)
+os.remove(temp_path)
--- /dev/null
+from dolfin import *
+import mshr
+import math
+
+
+r = mshr.Rectangle(Point(0,-.5), Point(4,.5))
+
+# Trivial inside outside
+assert r.inside(Point( .5, .0))
+assert not r.inside(Point(1.5, 1.5))
+
+rotated = mshr.CSGRotation(r, math.pi/2)
+assert not rotated.inside(Point(1.5, 1.5))
+assert rotated.inside(Point(0., 1.5))
+assert not rotated.inside(Point(0., 4.5))
+assert not rotated.inside(Point(0., -.5))
+
+# Translation
+translated = mshr.CSGTranslation(rotated, Point(2, 1))
+assert translated.inside(Point(2, 1.5))
+assert not translated.inside(Point(0, 1.5))
+
+# Circle
+c = mshr.Circle(Point(2, 1), .5)
+assert c.inside(Point(2, 1))
+assert not c.inside(Point(3, 1))
+
+c_rotated = mshr.CSGRotation(c, Point(2,1), math.pi)
+assert c_rotated.inside(Point(2,1))
+assert not c_rotated.inside(Point(3, 1))
+
+c_rotated_2 = mshr.CSGRotation(c_rotated, pi/2)
+assert not c_rotated_2.inside(Point(2,1))
+assert c_rotated_2.inside(Point(-1,2))
+
+# Rectangle and box with the vertices not ordered
+assert mshr.Rectangle(Point(0,0),Point(-1,-1)).inside(Point(-0.5,-0.5))
+#assert mshr.Box(Point(0,0,0),Point(-1,-1,-1)).inside(Point(-0.5,-0.5,-0.5))
--- /dev/null
+from dolfin import *
+from mshr import *
+
+# Polygon with list of points as argument
+p = Polygon([Point(0, 0), Point(1, 1), Point(0, 1)])
+
+# Polygon with tuple of points as argument
+p = Polygon( (Point(0, 0), Point(1, 1), Point(0, 1)) )
+
--- /dev/null
+from mshr import *
+from dolfin import *
+import math
+
+epsilon = 1e-11
+
+def test_volume_exact(g, exact) :
+ domain = CSGCGALDomain3D(g)
+ vol = domain.volume()
+ assert abs(vol-exact) < epsilon, "Computed volume of {} was {}, but expected {}.".format(g.str(False), vol, exact)
+
+def test_volume_bounds(g, lower, upper) :
+ domain = CSGCGALDomain3D(g)
+ vol = domain.volume()
+ assert vol <= upper and vol >= lower, "Computed volume of {} was {}, but expected to be in [{}, {}]".format(g.str(False), vol, lower, upper)
+
+
+### Test volume of primitives
+# Sphere
+test_volume_bounds(Sphere(Point(1,1,1), 1), 3.8/3.*math.pi, 4./3.0*math.pi)
+
+# TODO: Add primitives
+
+# Test union
+test_volume_exact(Box(Point(0,0,0), Point(2,2,2)) + Box(Point(-1,-1,-1), Point(1,1,1)), 15)
+
+# TODO: Add operators
+
+# Test translation
+c = Cylinder(Point(-1, 1, 2), Point(3.5, -1, 2), 2, 3)
+test_volume_exact(c, CSGCGALDomain3D(CSGTranslation(c, Point(2, 4, -5))).volume())
+
+# Test rotation
+e = Ellipsoid(Point(1,2,3), 2,4,6)
+test_volume_exact(e, CSGCGALDomain3D(CSGRotation(e, Point(1,1,1), math.pi/2.)).volume())
+
+# Test scaling
+s = Sphere(Point(0,0,0), 2)
+test_volume_exact(CSGScaling(s, 2), CSGCGALDomain3D(s).volume()*8)
--- /dev/null
+// Copyright (C) 2017 Benjamin Kehlet
+//
+// This file 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 3 of the License, or
+// (at your option) any later version.
+//
+// This file 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 DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <dolfin/mesh/Mesh.h>
+#include <dolfin/generation/RectangleMesh.h>
+#include <mshr/CSGGeometries3D.h>
+
+int main(int /* argc */, char** /* argv */)
+{
+ std::shared_ptr<dolfin::Mesh> m =
+ std::make_shared<dolfin::RectangleMesh>( dolfin::Point(1., 2.), dolfin::Point(2., 4.),
+ 5, 6);
+
+ std::shared_ptr<mshr::CSGGeometry> g = mshr::CSGGeometries::import_mesh(m);
+
+ return 0;
+}
--- /dev/null
+import mshr
+import tempfile, os, sys
+
+# A simple unit cube but with some vertices perturbed slightly to test the FuzzyPointMap
+cube = """
+solid ascii
+facet normal 0 0 0
+ outer loop
+ vertex 1e-12 0 1
+ vertex 0 1 0
+ vertex 0 -1e-11 0
+ endloop
+endfacet
+
+facet normal 0 0 0
+outer loop
+ vertex 0 0 1
+ vertex 0 1 1
+ vertex 0 1 1e-13
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 0 0 1
+ vertex 1 0 1
+ vertex 0 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 0 1
+ vertex 1 1 1
+ vertex 0 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 0 1
+ vertex 1 0 0
+ vertex 1 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 0 0
+ vertex 1 1 0
+ vertex 1 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 0 0
+ vertex 0 0 0
+ vertex 1 1 0
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 0 0 0
+ vertex 0 1 0
+ vertex 1 1 0
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 1 1
+ vertex 1 1 0
+ vertex 0 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 1 1 0
+ vertex 0 1 0
+ vertex 0 1 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 0 0 1
+ vertex 0 0 0
+ vertex 1 0 1
+endloop
+endfacet
+facet normal 0 0 0
+outer loop
+ vertex 0 0 0
+ vertex 1 0 0
+ vertex 1 0 1
+endloop
+endfacet
+endsolid
+"""
+
+# In python 3 convert the text from unicode to a "classic" str
+if sys.version_info[0] >= 3 :
+ cube = cube.encode("ascii")
+
+fd, filename = tempfile.mkstemp(suffix=".stl")
+os.write(fd, cube)
+os.close(fd)
+
+s = mshr.Surface3D(filename)
+d = mshr.CSGCGALDomain3D(s)
+
+os.remove(filename)
+
+assert d.num_holes() == 0
--- /dev/null
+import mshr
+from dolfin import *
+
+# issue 37
+g = mshr.Rectangle(Point(0.0, 0.0), Point(2.2, .41)) - mshr.Circle(Point(.2, .2), .05, 40)
+m = mshr.generate_mesh(g, 50)
+
+# issue 41 (failed only in parallel)
+c = mshr.Extrude2D(mshr.Circle(Point(0, 0, 0), 1), 1)
+m = mshr.generate_mesh(c, 10)
--- /dev/null
+from dolfin import *
+import mshr
+
+# Unit sphere mesh
+m = mshr.UnitSphereMesh(4)
+m = mshr.UnitSphereMesh(10)
--- /dev/null
+from dolfin import *
+import mshr
+
+# The ellipse is completely contained in the circle and will not be
+# visible in the resulting mesh, but it is so small that with
+# mesh_resolution = 10 that when converting it to a polygon the number
+# of segments in the polygon will be 0 (ie. the polygon is degenerate)
+# if not handled correctly.
+c = mshr.Circle(Point(0, 0), 1.5)
+e = mshr.Ellipse(Point(.05, 0), .01, .05)
+
+mesh = mshr.generate_mesh(c+e, 10)
+
--- /dev/null
+// Copyright (C) 2013 Benjamin Kehlet
+//
+// This file 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 3 of the License, or
+// (at your option) any later version.
+//
+// This file 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 DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// First added: 2013-10-03
+// Last changed: 2013-10-03
+
+#include <intersection_segments.h>
+#include <PolyhedronFactory.h>
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/Polyhedron_3.h>
+#include <CGAL/IO/Polyhedron_iostream.h>
+#include <iostream>
+
+typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
+typedef Kernel::Point_3 Point;
+typedef Kernel::Triangle_3 Triangle;
+typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
+typedef typename Polyhedron::Facet_handle Facet_handle;
+typedef typename Polyhedron::Halfedge_handle Halfedge_handle;
+typedef typename Kernel::Segment_3 Segment;
+
+void two_tetrahedrons()
+{
+ Polyhedron a;
+
+ make_tetrahedron(a,
+ Point(1.0, 0.0, 0.0),
+ Point(2.0, 0.0, 0.0),
+ Point(1.5, 1.0, 0.0),
+ Point(1.5, .5, 10.0));
+
+ Polyhedron b;
+ make_tetrahedron(b,
+ Point(0.0, 0., .5),
+ Point(0.0, 0.0, 1.5),
+ Point(0.0, 1.0, 1.0),
+ Point(10.0, .5, 1.0));
+
+ if (a.is_pure_triangle())
+ std::cout << "a is pure triangle" << std::endl;
+
+ if (b.is_pure_triangle())
+ std::cout << "b is pure triangle" << std::endl;
+
+ Polyhedron &biggest = a.size_of_facets() > b.size_of_facets() ? a : b;
+ Polyhedron &smallest = a.size_of_facets() > b.size_of_facets() ? b : a;
+
+ std::list<std::list<boost::tuple<Facet_handle, Facet_handle, Segment> > > polylines;
+ {
+ std::list<boost::tuple<Facet_handle, Facet_handle, Segment> > intersections;
+ compute_intersections(biggest, smallest, std::back_inserter(intersections));
+
+ for (std::list<boost::tuple<Facet_handle, Facet_handle, Segment> >::iterator it = intersections.begin();
+ it != intersections.end(); it++)
+ {
+ {
+ Halfedge_handle h = it->get<0>()->halfedge();
+ Triangle t(h->vertex()->point(), h->next()->vertex()->point(), h->next()->next()->vertex()->point());
+ assert(t.has_on(it->get<2>().source()));
+ assert(t.has_on(it->get<2>().target()));
+ }
+ {
+ Halfedge_handle h = it->get<1>()->halfedge();
+ Triangle t(h->vertex()->point(), h->next()->vertex()->point(), h->next()->next()->vertex()->point());
+ assert(t.has_on(it->get<2>().source()));
+ assert(t.has_on(it->get<2>().target()));
+ }
+ }
+ sort_polylines<Polyhedron>(biggest, smallest, intersections, polylines);
+ }
+
+ std::list<std::vector<typename Polyhedron::Halfedge_handle> > intersection_list;
+
+ split_facets<Polyhedron, 0>(biggest, polylines, intersection_list);
+ //split_facets<Polyhedron, 1>(smallest, polylines);
+
+}
+
+void two_boxes()
+{
+ Polyhedron a;
+ make_box(0,0,0, 4, 5, 2, a);
+
+ Polyhedron b;
+ make_box(1, 1, -1, 2, 2, 1, b);
+
+ if (a.is_pure_triangle())
+ std::cout << "a is pure triangle" << std::endl;
+
+ if (b.is_pure_triangle())
+ std::cout << "b is pure triangle" << std::endl;
+
+ Polyhedron &biggest = a.size_of_facets() > b.size_of_facets() ? a : b;
+ Polyhedron &smallest = a.size_of_facets() > b.size_of_facets() ? b : a;
+
+ std::list<std::list<boost::tuple<Facet_handle, Facet_handle, Segment> > > polylines;
+ {
+ std::list<boost::tuple<Facet_handle, Facet_handle, Segment> > intersections;
+ compute_intersections(biggest, smallest, std::back_inserter(intersections));
+
+ for (std::list<boost::tuple<Facet_handle, Facet_handle, Segment> >::iterator it = intersections.begin();
+ it != intersections.end(); it++)
+ {
+ {
+ Halfedge_handle h = it->get<0>()->halfedge();
+ Triangle t(h->vertex()->point(), h->next()->vertex()->point(), h->next()->next()->vertex()->point());
+ assert(t.has_on(it->get<2>().source()));
+ assert(t.has_on(it->get<2>().target()));
+ }
+ {
+ Halfedge_handle h = it->get<1>()->halfedge();
+ Triangle t(h->vertex()->point(), h->next()->vertex()->point(), h->next()->next()->vertex()->point());
+ assert(t.has_on(it->get<2>().source()));
+ assert(t.has_on(it->get<2>().target()));
+ }
+ }
+ sort_polylines<Polyhedron>(biggest, smallest, intersections, polylines);
+ }
+
+ std::list<std::vector<Halfedge_handle> > a_edges;
+ split_facets<Polyhedron, 0>(biggest, polylines, a_edges);
+ check_splitting<Polyhedron, 0>(biggest, polylines, a_edges);
+ //split_facets<Polyhedron, 1>(smallest, /* smallest, */ polylines);
+}
+
+int main(int argc, char** argv)
+{
+ two_boxes();
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg
+//
+// This file is part of DOLFIN.
+//
+// DOLFIN 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 3 of the License, or
+// (at your option) any later version.
+//
+// DOLFIN 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 DOLFIN. If not, see <http://www.gnu.org/licenses/>.
+//
+// Modified by Benjamin Kehlet, 2012
+// Modified by Johannes Ring, 2012
+// Modified by Joachim B Haga, 2012
+
+#include <dolfin.h>
+#include <mshr.h>
+
+int main(int argc, char** argv)
+{
+ // Define 3D geometry
+ mshr::Box box(0, 0, 0, 1, 1, 1);
+ mshr::Sphere sphere(dolfin::Point(0, 0, 0), 0.3);
+ mshr::Cone cone(dolfin::Point(0, 0, -1), dolfin::Point(0, 0, 1), .5, .5);
+
+ const boost::shared_ptr<mshr::CSGGeometry> g3d = box + cone - sphere;
+
+ // Test printing
+ dolfin::info("\nCompact output of 3D geometry:");
+ dolfin::info(*g3d);
+ dolfin::info("\nVerbose output of 3D geometry:");
+ dolfin::info(*g3d, true);
+
+ // Plot geometry
+ //dolfin::plot(g3d, "3D geometry (surface)");
+
+ // Generate and plot mesh
+ dolfin::Mesh mesh3d;
+
+ mshr::CSGMeshGenerator::generate(mesh3d, *g3d, 24);
+ dolfin::cout << "Done generating mesh" << dolfin::endl;
+ dolfin::info(mesh3d);
+ dolfin::plot(mesh3d, "3D mesh");
+
+ dolfin::interactive();
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2012 Anders Logg
+//
+// This file is part of mshr.
+//
+// mshr 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 3 of the License, or
+// (at your option) any later version.
+//
+// mshr 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 mshr. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <dolfin.h>
+#include <mshr.h>
+
+int main(int argc, char** argv)
+{
+ // Define 3D geometry
+ mshr::Box box(0, 0, 0, 1, 1, 1);
+ mshr::Sphere sphere(dolfin::Point(0, 0, 0), 0.3);
+ mshr::Cone cone(dolfin::Point(0, 0, -1), dolfin::Point(0, 0, 1), .5, .5);
+
+ const boost::shared_ptr<mshr::CSGGeometry> g3d = box + cone - sphere;
+
+ // Test printing
+ dolfin::info("\nCompact output of 3D geometry:");
+ dolfin::info(*g3d);
+ dolfin::info("\nVerbose output of 3D geometry:");
+ dolfin::info(*g3d, true);
+
+ // Plot geometry
+ //dolfin::plot(g3d, "3D geometry (surface)");
+
+ // Generate and plot mesh
+ dolfin::Mesh mesh3d;
+
+ mshr::CSGMeshGenerator::generate(mesh3d, *g3d, 24);
+ dolfin::cout << "Done generating mesh" << dolfin::endl;
+ dolfin::info(mesh3d);
+ dolfin::plot(mesh3d, "3D mesh");
+
+ dolfin::interactive();
+
+ return 0;
+}
--- /dev/null
+#
+# This file sets up include directories, link directories, and
+# compiler settings for a project to use mshr. It should not be
+# included directly, but rather through the DOLFIN_USE_FILE setting
+# obtained from mshr-config.cmake.
+#
+
+if (NOT MSHR_USE_FILE_INCLUDED)
+ set(MSHR_USE_FILE_INCLUDED 1)
+
+ # Add compiler definitions needed to use mshr
+ add_definitions(${MSHR_CXX_DEFINITIONS})
+
+ # Add compiler flags needed to use MSHR
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MSHR_CXX_FLAGS}")
+
+ # Add include directories needed to use MSHR
+ include_directories(${MSHR_INCLUDE_DIRS})
+ include_directories(SYSTEM ${MSHR_EXTERNAL_INCLUDE_DIRS})
+
+ # Add link directories needed to use MSHR
+ message(STATUS "Setting link directories")
+ link_directories(${MSHR_LIBRARIES_DIRS})
+endif()