Skip to content

Commit

Permalink
Added MeQuadBlossom and MeRemoveBadQuads classes.
Browse files Browse the repository at this point in the history
Added MeQuadBlossom and MeRemoveBadQuads classes.
Added MeWeightMatcher class containing the Quad Blossom algorithm that
MeQuadBlossom uses.
Added a Quad Blossom tutorial.
Added a static function to MeQuadBlossom to split all quads and triangles
into smaller quads.
  • Loading branch information
wdolinar committed Oct 2, 2018
1 parent f5a0db3 commit 6246521
Show file tree
Hide file tree
Showing 29 changed files with 10,068 additions and 34 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,14 @@ doc/_build
*.exe
*.out
*.app

# Doxygen
Doxygen/*.tag
Doxygen/html/*.css
Doxygen/html/*.html
Doxygen/html/*.js
Doxygen/html/*.png
Doxygen/html/search

# Testing files
*_out.2dm
65 changes: 36 additions & 29 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ else()
set(CMAKE_POSITION_INDEPENDENT_CODE True)
endif()

project(xmsmesh C CXX)

set(BUILD_TESTING NO CACHE BOOL "Enable/Disable testing")
set(IS_CONDA_BUILD NO CACHE BOOL "Set this if you want to make a conda package.")
set(CONDA_PREFIX "" CACHE PATH "Path to the conda environment used to build.")
set(IS_PYTHON_BUILD NO CACHE BOOL "Set this if you want to build the python bindings.")
set(XMSMESH_TEST_PATH "../test_files/" CACHE PATH "Path to test files for testing")
set(XMSMESH_TEST_PATH ${PROJECT_SOURCE_DIR}/test_files/ CACHE PATH "Path to test files for testing")
set(PYTHON_TARGET_VERSION 3.6 CACHE STRING "Version of python to link to for python wrapping.")

project(xmsmesh C CXX)

if(WIN32)
if(XMS_BUILD)
add_definitions(/D _WIN32_WINNT=0x0501) # Windows XP and higher
Expand Down Expand Up @@ -79,9 +79,11 @@ link_directories(${EXT_LIB_DIRS})

# Static library sources
set(xmsmesh_sources
xmsmesh/meshing/MeBadQuadRemover.cpp
xmsmesh/meshing/MeMeshUtils.cpp
xmsmesh/meshing/MeMultiPolyTo2dm.cpp
xmsmesh/meshing/MeMultiPolyMesher.cpp
xmsmesh/meshing/MeQuadBlossom.cpp
xmsmesh/meshing/detail/MeIntersectPolys.cpp
xmsmesh/meshing/detail/MePolyPatcher.cpp
xmsmesh/meshing/detail/MePolyOffsetter.cpp
Expand All @@ -91,18 +93,21 @@ set(xmsmesh_sources
xmsmesh/meshing/detail/MePolyRedistributePtsCurvature.cpp
xmsmesh/meshing/detail/MeRefinePtsToPolys.cpp
xmsmesh/meshing/detail/MeRelaxer.cpp
xmsmesh/meshing/detail/MeWeightMatcher.cpp
xmsmesh/meshing/MePolyMesher.cpp
xmsmesh/meshing/MePolyRedistributePts.cpp
xmsmesh/tutorial/TutMeshing.cpp
)

set(xmsmesh_headers
xmsmesh/meshing/MeBadQuadRemover.h
xmsmesh/meshing/MeMeshUtils.h
xmsmesh/meshing/MePolyMesher.h
xmsmesh/meshing/MeMultiPolyMesher.h
xmsmesh/meshing/MeMultiPolyMesherIo.h
xmsmesh/meshing/MeMultiPolyTo2dm.h
xmsmesh/meshing/MePolyRedistributePts.h
xmsmesh/meshing/MeQuadBlossom.h
xmsmesh/meshing/detail/MePolyCleaner.h
xmsmesh/meshing/detail/MePolyOffsetter.h
xmsmesh/meshing/detail/MePolyPts.h
Expand All @@ -112,6 +117,7 @@ set(xmsmesh_headers
xmsmesh/meshing/detail/MePolyRedistributePtsCurvature.h
xmsmesh/meshing/detail/MeRefinePtsToPolys.h
xmsmesh/meshing/detail/MeRelaxer.h
xmsmesh/meshing/detail/MeWeightMatcher.h
)

# Pybind11 sources
Expand All @@ -121,20 +127,29 @@ set(xmsmesh_py_source
# Meshing
xmsmesh/python/meshing/meshing_py.h
xmsmesh/python/meshing/meshing_py.cpp
xmsmesh/python/meshing/MeBadQuadRemover_py.cpp
xmsmesh/python/meshing/MeMeshUtils_py.cpp
xmsmesh/python/meshing/MeMultiPolyMesher_py.cpp
xmsmesh/python/meshing/MeMultiPolyMesherIo_py.cpp
xmsmesh/python/meshing/MePolyMesher_py.cpp
xmsmesh/python/meshing/MeMultiPolyTo2dm_py.cpp
xmsmesh/python/meshing/MePolyMesher_py.cpp
xmsmesh/python/meshing/MePolyRedistributePts_py.cpp
xmsmesh/python/meshing/MeQuadBlossom_py.cpp
)

# Tests
if (BUILD_TESTING)
add_definitions(-DXMSMESH_TEST_PATH="${XMSMESH_TEST_PATH}/")
add_definitions(-DCXX_TEST -DCXXTEST4)
add_definitions(-DXMSMESH_TEST_PATH="${XMSMESH_TEST_PATH}/")
add_definitions(-DCXX_TEST -DCXXTEST4)

list(APPEND xmsmesh_sources
list(APPEND xmsmesh_sources
xmsmesh/meshing/MeBadQuadRemover.t.h
xmsmesh/meshing/MeMeshUtils.t.h
xmsmesh/meshing/MeMultiPolyTo2dm.t.h
xmsmesh/meshing/MePolyMesher.t.h
xmsmesh/meshing/MeMultiPolyMesher.t.h
xmsmesh/meshing/MePolyRedistributePts.t.h
xmsmesh/meshing/MeQuadBlossom.t.h
xmsmesh/meshing/detail/MePolyPaverToMeshPts.t.h
xmsmesh/meshing/detail/MeIntersectPolys.t.h
xmsmesh/meshing/detail/MePolyPatcher.t.h
Expand All @@ -143,26 +158,22 @@ if (BUILD_TESTING)
xmsmesh/meshing/detail/MePolyRedistributePtsCurvature.t.h
xmsmesh/meshing/detail/MeRefinePtsToPolys.t.h
xmsmesh/meshing/detail/MeRelaxer.t.h
xmsmesh/meshing/MeMeshUtils.t.h
xmsmesh/meshing/MeMultiPolyTo2dm.t.h
xmsmesh/meshing/MePolyMesher.t.h
xmsmesh/meshing/MeMultiPolyMesher.t.h
xmsmesh/meshing/MePolyRedistributePts.t.h
xmsmesh/meshing/detail/MeWeightMatcher.t.h
xmsmesh/tutorial/TutMeshing.t.h
)
)

find_package(CxxTest)
if(CXXTEST_FOUND)
include_directories(${CXXTEST_INCLUDE_DIRS})
enable_testing()

set(CXXTEST_TESTGEN_ARGS --xunit-printer --have-eh)
file(GLOB_RECURSE test_headers ${CMAKE_CURRENT_LIST_DIR}/xmsmesh/*.t.h)
CXXTEST_ADD_TEST(
runner runner.cpp ${test_headers}
)
target_link_libraries(runner ${PROJECT_NAME})
endif()
find_package(CxxTest)
if(CXXTEST_FOUND)
include_directories(${CXXTEST_INCLUDE_DIRS})
enable_testing()

set(CXXTEST_TESTGEN_ARGS --xunit-printer --have-eh)
file(GLOB_RECURSE test_headers ${CMAKE_CURRENT_LIST_DIR}/xmsmesh/*.t.h)
CXXTEST_ADD_TEST(
runner runner.cpp ${test_headers}
)
target_link_libraries(runner ${PROJECT_NAME})
endif()
endif ()

# Static library
Expand Down Expand Up @@ -228,7 +239,3 @@ foreach (header IN LISTS xmsmesh_headers)
DESTINATION "include/${subdir}"
)
endforeach ()




4 changes: 3 additions & 1 deletion Doxygen/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,9 @@ SKIP_FUNCTION_MACROS = YES
# 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 = xmsinterp.tag=https://aquaveo.github.io/xmsinterp/ xmscore.tag=https://aquaveo.github.io/xmscore/
TAGFILES = xmsinterp.tag=https://aquaveo.github.io/xmsinterp/ \
xmsgrid.tag=https://aquaveo.github.io/xmsgrid/ \
xmscore.tag=https://aquaveo.github.io/xmscore/

# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
Expand Down
22 changes: 22 additions & 0 deletions Doxygen/Meshing_Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,25 @@ This is another example of how to mesh a single polygon with a hole, and in this
An image of the 2d mesh generated from this example is shown below. The *.2dm file for this 2d mesh can be found at files_xmsmesh/Test/Tutorial_Meshing/Example_SpringRelax_base.2dm.

![2d mesh generated from simple polygon with a hole and boundary spacing = 10.0, using spring relaxation](tutMesh_SpringRelax_Output.png)

## Example - Triangular to quad mesh using Quad Blossom and Bad Quad Remover {#Example_Quad_Blossom}

This example shows how to convert a triangular mesh to a quad mesh using the Quad Blossom algorithm. Then the xms::MeBadQuadRemover class is used to remove narrow, ill-formed quads while still preserving the basic sizes of cells.

There are two arguments to MeQuadBlossom::MakeQuads(). The first is a bool: splitVertices. If this is false, the algorithm will not attempt to match triangles on the boundary that share a boundary point but not an edge; that is, there is at least one other triangle between them that also shares that boundary point. So by setting this argument to false, you may be left with more than one triangle in the mesh, even if the number of boundary edges is even. If this argument is true, it will match such triangles and then turn them into quads by adding a new point on the interior of the mesh and an edge between that point and the boundary point. The two matching boundary triangles will add and share that edge, thus becoming quads. If the number of triangles incident to the boundary point to be split is odd, the new point is placed at the centroid of the middle triangle; if even, it is at the mid-point of the middle edge. The other cells incident to the boundary point will swap to use the new split point instead. So with this argument set to true, you may see some added points in the output ugrid. Internally, the algorithm weights the match on such adjacent boundary triangles lower than any interior edges, so the interior edges will get matched and eliminated before splitting any boundary points to form new edges. If set to false, the output quad cells will just be the result of removing prior interior edges so that pairs of triangles become quads.

The second argument is also a bool: useAngle. There are two internal methods used to associate a weight for each interior edge (between adjacent triangles). Both methods are based on the quad that would be formed by eliminating that edge. If useAngle is true, the weight is based on the interior angles of the quad (and in particular the one farthest from being a right angle. If false, the weight is based on the ratio of the sum of the squares of the lengths of each pair of adjacent sides to the length of their corresponding diagonal squared. Again, it ends up being based on the angle with the maximum deviation from 90 degrees. In both cases, we multiply the floating point weight (that is between 0 and 1) by 1,000 to get an integer weight between 0 and 1,000. (The weights between boundary triangles that might be split are all set to -10.) In general, the closer the quad is to rectangular, the closer the weight is to the maximum, and as the aspect ratio gets larger, the closer the weight gets to zero. They produce slightly different results, but we don't have enough experience yet to recommend one over the other.

Also, note that the algorithm is O(N^3) with respect to the number of points in the mesh. Hence, we provide a function to estimate the runtime. In general, if your mesh has over a 2,000 points, you should split it at convenient boundaries to get below that limit and then run the algorithm on each submesh. You should get no triangles in the result if the sum of all of the boundary edges is an even number and you set splitVertices to true. The main purpose of PreMakeQuads() is to find the edges and return the number of boundary edges, so you can check that it is even. This is demonstrated in the example below.

![Triangular mesh input to Quad Blossom algorithm](tutMesh_QuadBlossom_Input.png)

\snippet xmsmesh/tutorial/TutMeshing.cpp snip_test_Example_QuadBlossom_BadQuadRemover

![Output mesh input after running Quad Blossom algorithm](tutMesh_QuadBlossom_Output.png)

![Output mesh input after running Quad Blossom and Bad Quad Remover first time](tutMesh_QuadBlossom_BadQuadRemover_Output.png)

![Output mesh input after running Bad Quad Remover second time](tutMesh_QuadBlossom_BadQuadRemover_Output2.png)

Running an angle relaxer should improve the mesh even more. As shown, you can remove bad quads several times, but each time will result in larger quads. It isn't clear that several such runs significantly improves the mesh.
95 changes: 95 additions & 0 deletions _clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
BasedOnStyle: Google
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments doesn't seem to do anything, unfortunately
FixNamespaceComments: true
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 1
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: false
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 8
UseTab: Never
...
5 changes: 5 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def configure(self):
self.options['xmsinterp'].pybind = self.options.pybind
self.options['xmsinterp'].testing = self.options.testing

self.options['xmsgrid'].xms = self.options.xms
self.options['xmsgrid'].pybind = self.options.pybind
self.options['xmsgrid'].testing = self.options.testing

if s_compiler == "apple-clang" and s_os == 'Linux':
raise ConanException("Clang on Linux is not supported.")

Expand All @@ -62,6 +66,7 @@ def requirements(self):

self.requires("xmscore/[>=1.0.33]@aquaveo/stable")
self.requires("xmsinterp/[>1.0.7]@aquaveo/stable")
self.requires("xmsgrid/[>=1.0.10]@aquaveo/stable")

def build(self):
cmake = CMake(self)
Expand Down
2 changes: 2 additions & 0 deletions condabuildinfo.cmake
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
set(EXT_INCLUDE_DIRS
"${CMAKE_INSTALL_PREFIX}/include/xmscore"
"${CMAKE_INSTALL_PREFIX}/include/xmsinterp"
"${CMAKE_INSTALL_PREFIX}/include/xmsgrid"
)
set(EXT_LIB_DIRS "${CMAKE_INSTALL_PREFIX}/lib")
set(EXT_LIBS
xmscore
xmsinterp
xmsgrid
)

set(Boost_USE_STATIC_LIBS TRUE)
Expand Down
3 changes: 2 additions & 1 deletion generateDocumentationAndDeploy.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ echo 'Generating Doxygen code documentation...'
cd $(dirname $DOXYFILE)
wget https://raw.githubusercontent.com/Aquaveo/xmscore/gh-pages/xmscore.tag
wget https://raw.githubusercontent.com/Aquaveo/xmsinterp/gh-pages/xmsinterp.tag
wget https://raw.githubusercontent.com/Aquaveo/xmsgrid/gh-pages/xmsgrid.tag
doxygen $DOXYFILE 2>&1 | tee doxygen.log
# ensure that we do not have doxygen warnings
if [ -s 'doxy_warn.log' ]; then cat doxy_warn.log && exit 1; fi;
Expand All @@ -87,7 +88,7 @@ if [ -s 'doxy_warn.log' ]; then cat doxy_warn.log && exit 1; fi;
if [ -d "html" ] && [ -f "html/index.html" ]; then

# don't upload docs unless we are in "MASTER"
if [$TRAVIS_BRANCH -eq "master"]
if [ $TRAVIS_BRANCH -eq "master" ]
then
mv xmsmesh.tag "$TRAVIS_BUILD_DIR/code_docs/$GH_REPO_NAME/"
mv html/* "$TRAVIS_BUILD_DIR/code_docs/$GH_REPO_NAME/"
Expand Down
Loading

0 comments on commit 6246521

Please sign in to comment.